ubospica commited on
Commit
a9c7ffc
·
verified ·
1 Parent(s): 8399c74

Add standalone English precision analysis

Browse files

Explain baseline and current arithmetic, matched B300 measurements, and the individual-state KDA outlier; link the document from README.

Files changed (2) hide show
  1. PRECISION_ANALYSIS.md +284 -0
  2. README.md +10 -27
PRECISION_ANALYSIS.md ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Precision Analysis: TileLang Baselines and CuTe DSL Kernels
2
+
3
+ This document compares the original TileLang baselines with the released GDN
4
+ and KDA CuTe DSL implementations. It describes unchanged formats, arithmetic
5
+ changes, precision improvements, deliberate approximations, and measured limits.
6
+ The paired measurements were collected on NVIDIA B300 on 2026-10-26.
7
+
8
+ The output format and the original benchmark thresholds are unchanged. The
9
+ current implementations retain the rebuilt state and residual in FP32 Tensor
10
+ Memory (TMEM), while using reduced-precision operands and selected approximations
11
+ elsewhere. FP32 storage or accumulation does not imply FP32 precision at every
12
+ operation, or bitwise equivalence to the mathematical reference.
13
+
14
+ ## 1. Three different meanings of precision
15
+
16
+ The following are separate implementations and measurement roles:
17
+
18
+ 1. **Mathematical reference:** `definition.json` computes the flush mainly with
19
+ FP32 PyTorch operations. It still rounds the new rank-4 factors to FP16 and
20
+ quantizes the residual to INT8. Its decoded output already has format error.
21
+ 2. **Original TileLang baseline:** already uses mixed precision, including
22
+ FP16/BF16 tensor-core operands, FP32 accumulation, FP16 intermediate storage,
23
+ and FP32 corrections. It is not an all-FP32 implementation.
24
+ 3. **Current CuTe DSL implementation:** also uses mixed precision. It changes
25
+ which intermediates retain FP32, how products are compensated, and where
26
+ lower-precision approximations are accepted.
27
+
28
+ The judge rebuilds a separate target state in FP64 from the same supplied
29
+ inputs. These inputs already contain INT8, FP16, and BF16 values. This target is
30
+ not an unquantized model state from before those input conversions. FP64 is used
31
+ for validation, not by the production flush arithmetic.
32
+
33
+ ## 2. Input and output formats are unchanged
34
+
35
+ The fixed geometry is 32 heads, a 128 by 128 state, a 16-entry update window,
36
+ and rank 4.
37
+
38
+ | Data | Original baseline | Current implementation |
39
+ |---|---|---|
40
+ | Residual `codes` | INT8, values in [-127, 127] | Same |
41
+ | Checkpoint scales `s_k`, `s_v` | FP32 | Same |
42
+ | Low-rank factors `u`, `q` | FP16 | Same |
43
+ | Update buffers `kbuf`, `ubuf` | BF16 | Same |
44
+ | Weights `w`, GDN scalar gate `p` | FP32 | Same |
45
+ | KDA gate history `gbuf` | FP16 | Same |
46
+ | KDA cumulative log-gate `pcum` | FP32 | Same |
47
+ | Supplied starting basis `q0` | FP32 | Same |
48
+
49
+ The checkpoint continues to decode as
50
+
51
+ $$
52
+ S_{\mathrm{old}}[v,k]
53
+ =\sum_{r=1}^{4}q[r,v]u[r,k]
54
+ +C[v,k]\,s_k[k]s_v[v]/127.
55
+ $$
56
+
57
+ The rank, window length, supplied starting basis, and one round of subspace
58
+ iteration are retained. The new factors are rounded to FP16 before forming the
59
+ final residual, so that the residual compensates for the factors actually stored.
60
+ INT8 codes retain nearest rounding and the same value range.
61
+
62
+ `Pool.raw` is an FP32 backing allocation with typed views into its storage.
63
+ That backing dtype does not make its INT8 codes or FP16 factors FP32 data.
64
+
65
+ ## 3. State and residual storage
66
+
67
+ ### GDN baseline
68
+
69
+ The baseline first forms a partial state `B` containing the dequantized old
70
+ residual and buffered updates, then stores `B` in FP16 shared memory. The old
71
+ rank-4 term is carried separately through FP32 algebraic corrections rather than
72
+ being irreversibly folded into that FP16 matrix. The final residual is computed
73
+ with FP32 accumulation, then stored in FP16 before scale calculation and coding.
74
+
75
+ This distinction matters: the baseline does not simply cast the entire state
76
+ to FP16 and discard the old low-rank correction.
77
+
78
+ ### KDA baseline
79
+
80
+ The default configuration has `KEXACT=True`. For the final residual it performs
81
+ another dequantization/rebuild from the staged inputs, accumulates the terms in
82
+ FP32, and applies the per-channel checkpoint decay in FP32. It preserves the
83
+ unscaled old factors for this residual calculation.
84
+
85
+ The residual is then scaled by a power of two per block and stored in FP16.
86
+ Scaling protects small residuals from FP16 underflow, but does not remove FP16
87
+ significand rounding. The projection stage still uses an FP16 partial state and
88
+ an FP16 copy of the decayed old factors.
89
+
90
+ ### Current implementations
91
+
92
+ Both kernels retain the full rebuilt state in FP32 TMEM. They also create a
93
+ scaled FP16 shared-memory copy for the projection products. After producing the
94
+ new FP16 factors, they subtract the low-rank product from the retained FP32
95
+ state and keep the residual in FP32 through the quantization passes.
96
+
97
+ This removes an important FP16 storage-rounding step from the final residual
98
+ path. It also lets KDA avoid the baseline's second full residual rebuild.
99
+ However, the retained FP32 state was itself formed using mixed-precision
100
+ products, so it is not necessarily the same bit pattern as a sequence of FP32
101
+ reference operations.
102
+
103
+ Residual accuracy is sensitive to cancellation: when `S` and the new low-rank
104
+ product are large but their difference is small, a rounding error measured
105
+ relative to `S` can be large relative to the residual.
106
+
107
+ ## 4. Product precision and high/low compensation
108
+
109
+ The original baselines already use BF16 high/low splitting for weighted or
110
+ decayed updates. Conceptually, a value is represented as
111
+
112
+ $$
113
+ x\approx x_{\mathrm{hi}}+x_{\mathrm{lo}},\qquad
114
+ Ax\approx Ax_{\mathrm{hi}}+Ax_{\mathrm{lo}}.
115
+ $$
116
+
117
+ The current GDN implementation folds the update weight into the key operand,
118
+ whereas the baseline primarily folds it into the value operand. The current GDN
119
+ high BF16 part is truncated and the low part compensates for the discarded
120
+ bits. These are mathematically equivalent organizations with different
121
+ floating-point rounding and accumulation orders.
122
+
123
+ The current kernels also use TF32 high/low compensation for scaled or decayed
124
+ old low-rank terms. This retains more information than a single TF32 conversion,
125
+ but is not a guarantee of exact FP32 multiplication or reduction.
126
+
127
+ The projection products primarily use FP16 operands with FP32 accumulation.
128
+ The fast projection path can still lose information in the FP16 state copy;
129
+ retaining a separate FP32 state improves the residual path without making the
130
+ computed low-rank subspace identical to the reference.
131
+
132
+ `f32x2` means two independent FP32 operations executed in a packed instruction.
133
+ It does not mean FP64, double-double arithmetic, or additional significant bits
134
+ for either value.
135
+
136
+ ## 5. Starting basis and dynamic range
137
+
138
+ The baseline converts `q0` directly to FP16 for the projection. The current GDN
139
+ kernel first rescales its columns by powers of two and represents the basis
140
+ with FP16 high and low parts. KDA also conditions the starting basis with
141
+ Cholesky orthogonalization before storing its high/low representation.
142
+
143
+ The current kernels choose a per-head power-of-two scale for state construction
144
+ and projection. The scale is undone when the factors and checkpoint scales are
145
+ written. For normal representable values, multiplication by a power of two
146
+ changes the exponent without introducing the same rounding as an arbitrary
147
+ scale factor. Range limits and subsequent FP16 conversion still matter.
148
+
149
+ KDA already used block scaling in the baseline's residual-storage stage. The
150
+ current implementation extends range management to the full state path while
151
+ retaining its FP32 main copy. It does not make FP16 and BF16 interchangeable.
152
+
153
+ ## 6. Orthogonalization changes
154
+
155
+ The baseline defaults to `GRAM16=False`: both CholeskyQR passes compute their
156
+ Gram matrices in FP32. A comment describing an optional FP16 second Gram pass
157
+ does not describe this default path.
158
+
159
+ The current kernels can skip the second orthogonalization pass when the first
160
+ pass meets a conditioning test. KDA performs the second pass when needed. GDN
161
+ also has a more conservative path with state-low-part compensation and repeated
162
+ Gram-Schmidt projections for difficult cases; its second Cholesky pass can
163
+ suppress numerically dependent directions.
164
+
165
+ These choices alter numerical results and are not merely scheduling changes.
166
+ Two QR passes also do not mean two rounds of the outer subspace-iteration
167
+ algorithm; these are different operations.
168
+
169
+ ## 7. Deliberate lower-precision approximations
170
+
171
+ Some current operations trade local precision for throughput:
172
+
173
+ - **GDN column sums:** parts of the absolute-residual column reduction use
174
+ FP16 packed addition before returning to FP32. The baseline reads an FP16
175
+ residual but accumulates those column sums in FP32.
176
+ - **KDA column-scale reciprocal:** the reciprocal is rounded to FP16. That same
177
+ rounded value is used for coding and to derive the corresponding FP32 output
178
+ scale. A scale stored in FP32 can therefore still reflect a lower-precision
179
+ internal computation.
180
+ - **Row normalization:** the current kernels primarily use approximate
181
+ reciprocals, whereas the baseline uses a correctly rounded reciprocal here.
182
+ GDN retains a more accurate fallback for extreme row scales.
183
+ - **Conditional QR:** skipping the second pass changes the amount of numerical
184
+ correction performed on well-conditioned inputs.
185
+
186
+ For example, ignoring zero-value guards and clamps, KDA's scaled residual
187
+ `E_prime` yields a column reciprocal approximately of the form
188
+
189
+ $$
190
+ r_h=\operatorname{FP16}\left(
191
+ \operatorname{rsqrt}(\operatorname{mean}_v|E_{\mathrm{prime}}|)
192
+ \right).
193
+ $$
194
+
195
+ Coding uses `r_h`, and the stored column scale is derived from its reciprocal
196
+ with the state scale undone. This keeps the encoded values and saved scales
197
+ consistent, while moving the quantization grid relative to a fully FP32 scale
198
+ calculation. Individual codes and scales can consequently differ from the
199
+ reference even when decoded-state error remains close to it.
200
+
201
+ ## 8. What the accuracy metrics measure
202
+
203
+ For the original benchmark, each head index has an error averaged over batch
204
+ items and matrix elements:
205
+
206
+ $$
207
+ e_h=\operatorname{mean}_{b,v,k}
208
+ \left|\operatorname{decode}(\mathrm{checkpoint})-S_{64}\right|.
209
+ $$
210
+
211
+ On the relative-error branch, the reported ratio is
212
+
213
+ $$
214
+ \rho_h=e_h^{\mathrm{implementation}}/e_h^{\mathrm{FP32\ reference}}.
215
+ $$
216
+
217
+ The original requirements are unchanged:
218
+
219
+ - If reference error is nonzero and at least `1e-4 * mean(abs(S64))`, candidate
220
+ error must be at most `1.01 * reference_error`.
221
+ - Otherwise, the bound is `1e-3 * mean(abs(S64))`.
222
+ - Outputs must be finite and codes must remain in [-127, 127].
223
+
224
+ The reference output is itself quantized. A ratio of 1.0004 means approximately
225
+ 0.04% more reconstruction error than that reference, not a state error of 0.04%.
226
+ This criterion does not require elementwise-identical codes, factors, or scales.
227
+
228
+ ## 9. Matched B300 measurements
229
+
230
+ Both implementations received identical tensors. The comparison used six batch
231
+ sizes (16, 32, 64, 128, 256, 512) and three input families: structured edge cases,
232
+ a second window starting from the reference checkpoint, and random in-domain
233
+ inputs. Each implementation passed all 18 original benchmark cases per kernel.
234
+
235
+ | Metric | GDN baseline | GDN current | KDA baseline | KDA current |
236
+ |---|---:|---:|---:|---:|
237
+ | Worst original-benchmark reference-relative error ratio | 1.00324455 | 1.00041398 | 1.00296773 | 1.00105573 |
238
+ | Maximum individual-state MAE / mean(abs(S64)) | 1.052615% | 0.576760% | 0.613289% | 0.612224% |
239
+ | Original cases passed | 18/18 | 18/18 | 18/18 | 18/18 |
240
+ | Failures in the additional individual-state check | 30/96,768 | 0/96,768 | 44/96,768 | 1/96,768 |
241
+
242
+ The individual-state check applies the same threshold formulas separately to
243
+ each `(batch item, head)`, including evaluating the relative/absolute branch
244
+ separately for that state. It does not average over batch items and is stricter
245
+ than the original benchmark. The failure counts in the last row are not failures
246
+ of the original aggregated scoring procedure.
247
+
248
+ ### Current KDA boundary case
249
+
250
+ One individual state in the batch-128 random case has a reference-relative MAE
251
+ ratio of **1.01256057**, exceeding the individual relative limit of 1.01. Its
252
+ original aggregated benchmark case passes. Therefore, the data supports better
253
+ measured original-benchmark ratios, but not a claim that every individual state
254
+ satisfies the same bound.
255
+
256
+ No stage-by-stage numerical ablation was performed. The isolated failure cannot
257
+ be assigned to the FP16 reciprocal, projection, conditional QR, or another
258
+ specific approximation from these measurements alone.
259
+
260
+ These are finite test results. They do not establish bounds for every possible
261
+ input, long sequences of self-generated checkpoints, or full-model perplexity.
262
+ In particular, the second-window test starts from a reference checkpoint rather
263
+ than a long chain of each implementation's own outputs.
264
+
265
+ ## 10. Evidence and reproduction
266
+
267
+ - Original mathematical definitions: [GDN](gdn/definition.json), [KDA](kda/definition.json).
268
+ - Unmodified TileLang baselines: [GDN](gdn/baseline.py), [KDA](kda/baseline.py).
269
+ - Released CuTe sources: [GDN](build/torch-cuda/_gdn.py), [KDA](build/torch-cuda/_kda.py).
270
+ - Matched measurements: [GDN](gdn/precision_comparison.json), [KDA](kda/precision_comparison.json).
271
+ - Earlier deployment validation: [GDN](gdn/validation_results.json), [KDA](kda/validation_results.json).
272
+ - Source hashes and provenance: [GDN](gdn/provenance.json), [KDA](kda/provenance.json).
273
+
274
+ Using the pinned [dependencies](requirements.txt), run on an idle B300:
275
+
276
+ ```bash
277
+ python compare_precision.py gdn
278
+ python compare_precision.py kda
279
+ ```
280
+
281
+ The [comparison script](compare_precision.py) creates the inputs once for both
282
+ implementations, rebuilds the FP64 target, invokes the original judge, and
283
+ records the extra individual-state check separately. It writes results to each
284
+ component's `precision_comparison.json`.
README.md CHANGED
@@ -57,33 +57,16 @@ and provenance. Dependency pins and Apache-2.0 licensing are shared at the root.
57
 
58
  ## Precision
59
 
60
- Output formats remain INT8 codes, FP32 scales, and FP16 rank-4 factors;
61
- update buffers remain BF16. Both baseline and current implementations use
62
- mixed-precision arithmetic. The current kernels keep the state and residual in
63
- FP32 TMEM, while projections use scaled FP16 operands and selected operations
64
- use local approximations. FP32 storage does not imply FP32 precision at every step.
65
-
66
- On 2026-10-26, both implementations were run on identical B300 inputs: six batch
67
- sizes and three input families. Each passed all 18 original benchmark cases.
68
- Ratios below compare decoded-state MAE to the FP32 reference's MAE, both measured
69
- against the same FP64 rebuilt state; the normal acceptance limit is 1.01.
70
-
71
- | Kernel | Baseline worst original-judge ratio | Current worst original-judge ratio | Baseline / current failures in extra individual-state check |
72
- |---|---:|---:|---:|
73
- | GDN | 1.00324455 | 1.00041398 | 30 / 0 |
74
- | KDA | 1.00296773 | 1.00105573 | 44 / 1 |
75
-
76
- The extra check independently tests 96,768 (batch item, head) states per kernel.
77
- The original judge averages over the batch dimension, so this extra check is
78
- stricter. The current KDA outlier occurs in batch 128 random inputs, with an
79
- individual reference-relative MAE ratio of 1.01256057. This exceeds 1.01 for that
80
- individual state even though the original aggregated benchmark passes. These
81
- results do not establish a universal bound on every possible input.
82
-
83
- Full records are in [gdn/precision_comparison.json](gdn/precision_comparison.json)
84
- and [kda/precision_comparison.json](kda/precision_comparison.json). Reproduce on
85
- an idle B300 with `python compare_precision.py gdn` and
86
- `python compare_precision.py kda` using the pinned dependencies.
87
 
88
  ## Reproduce
89
 
 
57
 
58
  ## Precision
59
 
60
+ See the standalone [precision analysis](PRECISION_ANALYSIS.md) for the original
61
+ and current arithmetic paths, deliberate approximations, matched measurements,
62
+ and validation limits. Input/output formats and original scoring thresholds
63
+ are unchanged; both implementations pass all 18 original comparison cases.
64
+
65
+ The additional individual-state check found one current KDA outlier in the
66
+ batch-128 random case (error ratio 1.01256057 against the individual limit 1.01).
67
+ The original benchmark averages the batch dimension; the extra check treats
68
+ individual states separately. Full records and reproduction commands are in
69
+ the analysis document.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  ## Reproduce
72