Reference: Kimi Linear tech report — https://github.com/MoonshotAI/Kimi-Linear/blob/master/tech_report.pdf
Code:
tokamax/_src/ops/experimental/kda/base.py— public KDA argument and recurrent-state contracttokamax/_src/ops/experimental/kda/reference.py— pure JAX recurrent reference implementationtokamax/_src/ops/experimental/kda/pallas_mosaic_tpu.py— Tokamax custom-VJP adaptertokamax/_src/ops/experimental/kda/pallas_mosaic_tpu_kernel.py— public low-level kernel entry pointstokamax/_src/ops/experimental/kda/pallas_mosaic_tpu_types.py— typedKdaResidualscontracttokamax/_src/ops/experimental/kda/pallas_mosaic_tpu_fwd_kernel.py— construction of forward residual matrices and saved statestokamax/_src/ops/experimental/kda/pallas_mosaic_tpu_bwd_kernel.py— backward orchestrator and Pallas kernelstokamax/_src/ops/experimental/kda/common.py— shared gate cumsum, state recurrence, and mini-batch helpertokamax/_src/ops/experimental/kda/cp_utils.py— context-parallel gradient mergetokamax/_src/ops/experimental/kda/utils.py— alignment, unalignment, and L2-normalization backward helpers
1. Goal and Data Flow of the Backward Pass
KDA processes the sequence in chunks of BT time steps, with BT=64 by default.
For each chunk, the forward output is the sum of two parts:
\mathbf{o}
= \underbrace{s\,(\mathbf{q}\odot 2^{\mathbf{g} })\,\mathbf{h} }_{\text{inter: reads historical state} }
+ \underbrace{\mathrm{tril}(\mathbf{A}_{qk})\,\mathbf{v}_{\text{new} } }_{\text{intra: reads earlier tokens within the chunk} }
Intuitively, the output of a query comes from two paths:
- inter path: reads information from the historical state
hthat already exists when entering this chunk; - intra path: reads information from the updated results of earlier tokens within this chunk.
The Tokamax entry is PallasMosaicTpuKimiDeltaAttentionVjp._fwd(...), which passes the typed forward residuals and output cotangents to chunk_kda_bwd_custom(...). The orchestrator receives the output gradient do and optional final-state gradient dht, and produces:
dq, dk, dv: gradients with respect to query/key/value;db: gradient with respect tobeta;dg: gradient with respect to the publicgateinput. The fused kernel's reverse cumsum first produces the gradient of the per-token activated gate before cumsum; whenuse_gate_in_kernel=True,kda_gate_bwd(...)maps that gradient further back to the raw public gate;dh0: gradient with respect to the initial state, returned only when the caller providesinitial_state;dA, dbias: gradients with respect toa_logand optionaldelta_time_biaswhen the gate activation is computed inside the kernel.
dht is an optional input representing the gradient of the final state. It is nonzero only when the final state continues to be used by a downstream loss; when an ordinary attention layer only consumes the output o, dht can be None and is treated as zero.
1.1 Forward Saved Values and Backward Recompute Boundary
The backward pass does not redo all forward computation from scratch. The current implementation divides the quantities needed by the backward pass into three categories.
Forward residuals saved:
Aqk: the intra-chunk query-key attention matrix, shape[H, B, T, BT];Akk: the WY inverse matrix, i.e.A_kk^{-1}, shape[H, B, T, BT];h: the hidden state at the start of each chunk, shape[H, B, NT, K, V]; the forward retains it inKdaResidualswhenrematerialize_for_backward=False.
Backward recompute:
w: WY effective key / erase weight;qg: gated query;kg: gated key;v_new: WY-corrected value.
Optional recompute:
- When
use_gate_in_kernel=True, both backward preparation paths currently recompute the post-cumsum gate fromg_org,a_log, and optionaldelta_time_biasviakda_gate_chunk_cumsum, including the saved-hpath where the forward residual currently also containsg_cumsum. - When
rematerialize_for_backward=True, the backward does not take the saved-hpath, but instead reruns the forward state recurrence to recoverhandv_new.
This is manual rematerialization within the Mosaic custom VJP, not a call to
jax.checkpoint or jax.remat.
1.2 WY Representation and Gate Semantics
The state update of the intra-chunk delta-rule is inherently serial: each token controls its write strength with beta, and a later token depends on the state updated by the earlier token.
The WY representation encodes this serial update into a lower-triangular matrix A_kk, and uses the forward-saved inverse matrix A = A_kk^{-1} to rewrite the intra-chunk computation into parallel matmuls:
\mathbf{u}=\mathbf{A}(\mathbf{v}\odot\boldsymbol\beta)
\mathbf{w}=\mathbf{A}(\mathbf{k}\odot\boldsymbol\beta\odot 2^{\mathbf{g} })
\mathbf{v}_{\text{new} }=\mathbf{u}-\mathbf{w}\,\mathbf{h}
Akk is the stored inverse (I+L)^{-1}. The strictly lower-triangular matrix L already depends on the row-wise beta, and beta is also multiplied explicitly into the value and gated-key right-hand sides used to construct u and w. Backward must therefore account for both paths by which beta affects the result.
In the equations below, g denotes the chunk-local cumulative gate in log2 space. This is an internal quantity, not the raw public gate argument. Therefore all decays are written as 2^(...) and computed in the kernel via exp2:
qg = q * 2^g: the query reads the historical state decayed from the chunk start to the current position;kg = k * 2^(g_C - g): after the key is written it must decay to the end of the chunk before it can enter the next chunk's state;2^g_C: the decay of the entire state crossing one chunk, whereg_Cis the cumulative gate at the end of the chunk.
The corresponding inter-chunk recurrence is:
\mathbf{h}^{[t+1]}
=2^{\mathbf{g}_C}\odot\mathbf{h}^{[t]}
+\mathrm{kg}^\top\mathbf{v}_{\text{new} }
1.3 API and Variable Reference
Entry point: tokamax/_src/ops/experimental/kda/pallas_mosaic_tpu.py::PallasMosaicTpuKimiDeltaAttentionVjp._fwd(...), followed by tokamax/_src/ops/experimental/kda/pallas_mosaic_tpu_kernel.py::chunk_kda_bwd_custom(...).
All main-path tensors use the head-first [H, B, T, ...] layout. The public KDA API and the Pallas backend already use this layout; there is no input transpose at the custom-VJP boundary. The backward consumes the aligned and optionally L2-normalized copies stored in KdaResiduals, rather than the replayed original tensors supplied by Tokamax's generic VJP contract.
Inputs and forward-saved values:
| Variable | Shape | Meaning |
|---|---|---|
public query, key / residual q, k |
[H, B, T, K] |
query / key |
public value / residual v |
[H, B, T, V] |
value |
beta |
[H, B, T] |
delta-rule write strength per token |
public gate |
[H, B, T, K] |
per-token gate input before chunk-local cumsum |
g_cumsum |
[H, B, T, K] / None |
internal post-cumsum gate in log2 space; recomputed when omitted |
g_org |
[H, B, T, K] / None |
retained raw gate when activation runs in the kernel |
a_log |
[H] / None |
gate activation parameter |
delta_time_bias |
[H*K] / None |
optional gate activation bias |
Aqk |
[H, B, T, BT] |
forward-saved intra-chunk attention matrix |
Akk |
[H, B, T, BT] |
forward-saved WY inverse matrix |
h |
[H, B, NT, K, V] |
hidden state at each chunk start, saved on fast path |
do |
[H, B, T, V] |
output gradient |
initial_state |
[B, N, H, K, V] / None |
initial state under the public KDA contract |
dht |
[B, N, H, K, V] / None |
final-state cotangent under the public KDA contract |
Backward intermediate quantities:
| Variable | Shape | Meaning |
|---|---|---|
u |
[H, B, T, V] |
WY effective value, exists only transiently inside the recompute kernel |
w |
[H, B, T, K] |
WY effective key / erase weight |
qg, kg |
[H, B, T, K] |
gated query / key |
v_new |
[H, B, T, V] |
value after WY and historical-state correction |
dAqk |
[H, B, T, BT] |
intra attention matrix gradient, from the dAv kernel |
Outputs:
| Variable | Shape | Meaning |
|---|---|---|
dq, dk, dg |
[H, B, T, K] |
query / key / public gate-input gradients |
dv |
[H, B, T, V] |
value gradient |
db |
[H, B, T] |
beta gradient |
dh0 |
[B, N, H, K, V] / None |
initial-state gradient, matching the public input shape |
dA, dbias |
[H] / [H*K] / None |
a_log / delta_time_bias gradients |
Static constraints:
- the delivered Mosaic config provides
BT=chunk_size=64; - prepared
Tmust be divisible byBT; variable-length inputs are aligned before residual construction; - the main path uses the log2 gate, i.e.
use_exp2=True; - non-CP execution supports the backend's delivered
K<=256contract, including non-128-alignedK/V; CP currently requires bothKandVto be multiples of 128.
The backward orchestrator can normalize an internal four-dimensional final-state cotangent by inserting an N=1 axis, but that is an implementation compatibility path. The public Tokamax KDA contract accepts and returns recurrent states in the five-dimensional [B, N, H, K, V] form. Fixed-length Pallas execution requires N=1.
2. Code-Based Call Chain and Kernel Structure
The current Tokamax call chain is:
PallasMosaicTpuKimiDeltaAttentionVjp._fwd
└─ chunk_kda_bwd_custom
├─ unpack KdaResiduals
├─ align do with the retained cu_seqlens/aligned_cu_seqlens
├─ restore derived CP metadata retained by forward
├─ Stage 0: recover forward intermediate quantities
│ ├─ if use_gate_in_kernel: kda_gate_chunk_cumsum(...)
│ ├─ saved-state path rematerialize_for_backward=False:
│ │ └─ fused_recompute_w_u_vnew_from_h_pallas(...)
│ │ → w, qg, kg, v_new
│ └─ low-memory path rematerialize_for_backward=True:
│ ├─ _recompute_w_u_fwd(...)
│ │ → w, u, qg, kg
│ └─ chunk_gated_delta_rule_fwd_h(...)
│ → h, v_new
├─ Stage 1: chunk_kda_bwd_dAv_kernel(...)
│ → dAqk, dv
├─ optional context parallel:
│ ├─ chunk_gated_delta_rule_bwd_dhu_pre_process(...)
│ │ → dS_ext, dM
│ ├─ all_gather_into_tensor(...)
│ ├─ _merge_dht(...)
│ └─ construct the dht used by the fusion kernel
├─ Stage 2-5: _fused_dhu_wy_intra_cumsum_pallas_jit(...)
│ → dq, dk, dv, db, dg, dh0
├─ optional gate backward:
│ └─ kda_gate_bwd(...)
│ → dg, dA, dbias
├─ optional l2norm_bwd(...) for dq/dk
├─ _unalign_output(...) for varlen dq/dk/dv/dg/db
└─ restore dh0 shape and input dtypes
Mathematically, the backward pass can be decomposed into six stages:
- recompute the forward intermediate quantities;
- compute the gradient of the intra term
Aqk @ v_new; - recur the state gradient
dhbackward along the chunk axis; - backpropagate the WY representation;
- backpropagate the intra-chunk attention;
- perform reverse cumsum on the gate gradient.
The saved-h main path maps these six stages onto three Pallas kernels:
recompute fusion → w, qg, kg, v_new
dAv → dAqk, dv
dhu/WY/intra/cumsum → dq, dk, dv, db, dg, dh0
The low-memory path selected by rematerialize_for_backward=True additionally calls _recompute_w_u_fwd and chunk_gated_delta_rule_fwd_h to rerun the forward state recurrence. Context-parallel preparation, gate backward, L2-normalization backward, and varlen unalignment are optional operations around the core kernels.
2.1 Recompute Fusion Kernel
Entry point: fused_recompute_w_u_vnew_from_h_pallas(...).
This kernel reads the saved h and the forward-saved Akk, and recomputes the following in parallel per chunk:
u = Akk @ (v * beta)
w = Akk @ (k * beta * 2^g)
v_new = u - w @ h
qg = q * 2^g
kg = k * 2^(g_C - g)
u exists only inside the kernel and is not written back to HBM after v_new is computed. Since h is already saved, v_new no longer depends on the cross-chunk forward state recurrence, so each chunk can be scheduled independently.
The low-memory path is used when h is not retained. It first recovers w, u, qg, kg via _recompute_w_u_fwd(...), then calls chunk_gated_delta_rule_fwd_h(...) to rerun the state recurrence and obtain h, v_new. This path reintroduces the inter-chunk serial dependency and trades compute for lower forward residual memory.
2.2 dAv Kernel
Entry point: chunk_kda_bwd_dAv_kernel(...).
This kernel only handles the intra output:
\mathrm{tril}(\mathbf{A}_{qk})\,\mathbf{v}_{\text{new} }
The corresponding backprop is:
d\mathbf{A}_{qk}
=s\,\mathrm{tril}(d\mathbf{o}\,\mathbf{v}_{\text{new} }^\top)
d\mathbf{v}_{\text{new} }
=\mathrm{tril}(\mathbf{A}_{qk})^\top d\mathbf{o}
In the code, scale is multiplied only once, when generating dAqk. The subsequent fusion kernel directly consumes the already-scaled dAqk, avoiding repeated scaling.
The stored Aqk already contains scale, so the value-gradient branch uses Aqk^T @ do without another factor. The tensor named dAqk is scaled before the fused intra backward because that kernel differentiates the underlying unscaled query-key relation directly. Although chunk_kda_bwd_dAv_kernel retains q and k in its public signature, the current launcher and kernel use only v, Aqk, do, scale, and tiling parameters.
2.3 dhu/WY/intra/cumsum Fusion Kernel
Entry point: _fused_dhu_wy_intra_cumsum_pallas_jit(...).
This kernel executes in reverse chunk order and fuses four kinds of work:
dhubackward recurrence Maintains the cross-chunk state gradientdh, accumulating contributions from the output path, the delta-update path, and the chunk-decay path into the same VMEM scratch.WY backward Backpropagates from
v_new = u - w @ h,u = Akk @ (v * beta),w = Akk @ (...)toq, k, v, beta, g, and obtains the gradient contribution toAkk.intra backward Consumes the
dAqkproduced by the dAv kernel, continues backpropagating the gradient of the intra-chunk attention matrix toq, k, beta, g, and accumulates with the WY results.reverse cumsum The forward gate is a chunk-local cumsum, so the backward pass needs a reverse cumsum. The current implementation keeps this inside the same kernel and no longer launches a separate cumsum kernel.
The chunk axis uses the arbitrary grid semantics to carry the backward recurrence state; the head and batch axes are parallel dimensions.
2.4 Optional Context Parallel
When context parallel is enabled, the backward pass inserts a cross-rank state-gradient merge between the dAv and fusion kernels.
The flow is:
chunk_gated_delta_rule_bwd_dhu_pre_process(...)scans only the first real local segment, because that is the segment that can receive state from an upstream rank, and produces:dS_ext: this rank's external contribution to the input state gradient;dM: this rank's backward transition-matrix chain product.
- Pack
dS_extanddMalong the last dimension and exchange them with a singleall_gather_into_tensor(...). _merge_dht(...)merges downstream ranks' contributions in furthest-to-nearest order using thepost_num_ranksandis_last_rankmetadata retained by forward.- Construct a
[B,N,H,K,V]dhtand place each batch element's merged state gradient in its last real local segment slot before entering the fused backward kernel.
The CP backward direction goes from downstream rank to upstream rank, determined by aligned segment_ids and rank metadata restored into ContextParallelMetadata. B>1 is handled independently per batch element. The CP contract does not accept an external initial_state or return an initial-state gradient.
2.5 Optional Gate Backward
When use_gate_in_kernel=True, chunk_kda_bwd_custom(...) calls kda_gate_bwd(...) after the core backward.
At this point the fusion kernel has already applied the chunk-local reverse cumsum, so its dg is the gradient with respect to the activated per-token gate before cumsum. kda_gate_bwd(...) then differentiates the activation and maps this gradient to the raw public gate, a_log, and optional delta_time_bias, returning dg, dA, and dbias respectively.
3. TPU Adaptation
The goal of these implementation choices is to make the backward path align as closely as possible with the TPU's MXU/VPU/HBM execution model.
3.1 log2 Gate and exp2
The cumulative gate consumed by the intra and state-recurrence kernels is represented in log2 space, so decays are uniformly written as 2^g. The public gate input is converted to this representation by the forward gate/cumsum stage; it should not be confused with the internal cumulative value.
This has two benefits:
- intra-chunk and inter-chunk decays can be combined via addition and subtraction in the exponent;
- the kernel uses the hardware
exp2, avoiding the repeated change of base ofexp.
3.2 fp32 Accumulation and bf16 Storage
Inputs are usually bf16, but matmuls use fp32 accumulate.
Inside the main fusion kernel, fp32 inputs select jax.lax.Precision.HIGHEST, while bf16 inputs use the default dot precision with fp32 accumulation. The reverse-cumsum dot explicitly uses jax.lax.Precision.HIGHEST for both input dtypes. These choices matter for dg, where cumulative gate contributions can nearly cancel.
3.3 VMEM Scratch Retains Cross-Stage Intermediates
The fusion kernel places the backward state dh in a [MB, K, V] VMEM scratch and updates it in reverse chunk order.
At the same time, dv_new, the WY intermediate gradients, the intra intermediate gradients, and the local results needed by the reverse cumsum all stay circulating within VMEM as much as possible. This avoids repeatedly writing back to and reading from HBM between the dhu, WY, intra, and cumsum logical stages.
3.4 Mini-Batch Controls DMA Granularity
A Pallas program processes MB head/chunk tiles at a time.
MB is selected from an estimated VMEM footprint, but the exact implementation differs by kernel. chunk_kda_bwd_dAv_kernel uses the shared estimate_mini_batch(...) helper. The saved-h recompute kernel, the main fusion kernel, and the CP pre-process currently use local VMEM-budget heuristics with kernel-specific caps and divisibility/alignment adjustments.
- too small leads to insufficient DMA granularity and low HBM bandwidth utilization;
- too large increases VMEM and register pressure and static unrolling overhead.
Therefore each launcher estimates its tile footprint and selects an MB subject to its grid, divisibility, and TPU minor-dimension alignment requirements. The saved-h recompute launcher can pad its flattened chunk count when no suitable divisor is available; the main fusion and CP launchers reduce MB until it divides the head count.
3.5 BlockSpec Data Movement and Explicit CP DMA
The three core saved-h kernels use Pallas grids and BlockSpec objects to describe HBM tiles. Their launchers do not contain an explicit hand-written async-copy loop.
The CP pre-process is different: _chunk_gated_delta_rule_bwd_dhu_pre_process_kernel is the active CP implementation and explicitly uses pltpu.make_async_copy, DMA semaphores, and double-buffered-VMEM inputs. It is a single-program reverse scan over head groups and chunks, overlapping the next input transfer with the current matrix work and asynchronously writing each head group's dS_ext/dM summary.
3.6 Tiling and Padding Principles
TPU tiling aligns certain trailing dimensions to hardware tile boundaries. Although padded elements are semantically zero, they still consume real bandwidth at the HBM and DMA level.
The current implementation follows a practical principle: introduce a more complex packed layout only when the HBM traffic that changing the layout can eliminate exceeds the extra reshape/gather/copy cost.
Therefore:
- non-CP backward supports the delivered unaligned
K/Vcases without imposing the CP lane constraint, while the CP pre-process requiresKandVto be multiples of 128; [BT, BT]small matrices such asAqkandAkkaccept the fixed padding brought by hardware alignment;- scalar trailing dimensions such as
beta/dbuse explicit singleton or two-dimensional layouts selected by each launcher.
In other words, padding itself is not necessarily a bad thing that must be eliminated. As long as the indexing and layout-conversion cost introduced by eliminating padding is higher, keeping the simple layout is actually faster.
3.7 Sub-block Normalization in the intra Backward
The intra backward involves decay terms like 2^(g_r - g_j). Since g is a cumulative quantity, directly subtracting and then exponentiating may cause numerical range problems.
The implementation splits the chunk into smaller sub-blocks and picks a reference gate within each sub-block for normalization. This way the exponent term is split into two parts relative to the reference point, keeping the intermediate values within a more controllable range.
The same sub-block structure also makes it convenient to organize the diagonal and off-diagonal blocks into a fixed number of batched matmuls, avoiding writing a quadratic loop over the sub-blocks.
3.8 Fixed Shapes Supporting varlen
TPU kernels require static shapes. Variable-length sequences do not trigger a separate ragged kernel, but still use fixed [B, T] tensors and a fixed (H // MB, B, NT) grid.
The adapter derives cu_seqlens, aligns every logical segment to BT, and retains both the original and aligned metadata in KdaResiduals. Backward first aligns do, then uses the retained aligned segment_ids and chunk mapping:
- chunks with segment id 0 are padding;
- the last chunk of each real segment seeds the backward recurrence with
dht; - the first chunk of each real segment outputs
dh0.
For illustration only, consider B=1, T=12, BT=4, holding two sequences of lengths 8 and 4. The delivered adapter still requires BT=64; the smaller value only makes the metadata example compact.
token segment_ids = [1,1,1,1, 1,1,1,1, 2,2,2,2]
chunk_seg_ids = [ 1, 1, 2 ]
chunk0 chunk1 chunk2
For fixed-length execution, _fused_dhu_wy_intra_cumsum_pallas_jit synthesizes an all-ones [B,T] segment-ID tensor, so every batch element is treated as one segment. After the core backward, variable-length token gradients are unaligned to the original [B,T_original] layout.
4. Fusion Performance Analysis
The measurements in this section are retained as historical optimization evidence. They predate the current Tokamax adapter, centralized alignment/residual contract, current CP metadata handoff, and some launcher-level naming changes. They must be rerun on the current commit before being used as an acceptance threshold or regression baseline.
Historical measurement configuration: TPU v6e, bf16, varlen seq_lens=[1800,1500,2000,1200,800], H=16, B=1, T=8192, K=V=128, BT=64. After chunking, the leading dim is 16 × 133.
4.1 Why Fusion Is the Main Optimization Direction
Most stages of the KDA backward have low arithmetic intensity and, when split apart individually, are easily HBM-bandwidth bound.
If each mathematical stage is turned into an independent kernel, a large number of intermediate tensors travel back and forth to HBM, for example:
w, qg, kg, v_new;dAqk, dv;dh, dv_new;- the WY and intra intermediate gradients;
- the input and output of the reverse cumsum.
The current implementation compresses these stages into three core Pallas kernels. The main benefit is not reducing the mathematical operations, but reducing the reads and writes at stage boundaries.
4.2 Historical Three-Kernel Main-Path Measurements
The core path consists of three parts:
Measurement: TPU v6e, bf16, varlen seq_lens=[1800,1500,2000,1200,800] (N=5 segments), H=16, B=1, T=8192, K=V=128, BT=64; after chunking the leading dim = 2128 = 16×133 (NT=133 = 128 base chunks + 5 segment paddings). HBM bandwidth taken as 1.6 TB/s.
| Kernel | Runtime (µs) | HBM (MB) | BW lower bound (µs) | BW utilization |
|---|---|---|---|---|
fused_recompute_w_u_vnew_from_h_pallas |
285.0 | 418.9 | 261.83 | 91.9% |
chunk_kda_bwd_dAv_kernel |
148.3 | 209.2 | 130.74 | 88.2% |
_fused_dhu_wy_intra_cumsum_pallas_jit |
900.1 | 951.8 | 594.9 | 66.1% |
Historically, the first two kernels showed that a simple compute structure and direct read/write pattern could utilize v6e HBM well for this workload.
In those measurements, the last fusion kernel was the main optimization target. Although it eliminates a large number of HBM round trips, it internally contains state recurrence, multiple intra-chunk matrix operations, gate gradient reductions, and reverse cumsum, so it mixes in compute, pipeline, and local layout overhead.
Measurement: TPU v7x, bf16, varlen seq_lens=[1800,1500,2000,1200,800] (N=5 segments), H=16, B=1, T=8192, K=V=128, BT=64; after chunking the leading dim = 2128 = 16×133 (NT=133 = 128 base chunks + 5 segment paddings). HBM bandwidth taken as 3.69 TB/s.
| Kernel | Runtime (µs) | HBM (MB) | BW lower bound (µs) | BW utilization |
|---|---|---|---|---|
fused_recompute_w_u_vnew_from_h_pallas |
181.675 | 418.9 | 113.52 | 62.5% |
chunk_kda_bwd_dAv_kernel |
100.237 | 209.2 | 56.69 | 56.6% |
_fused_dhu_wy_intra_cumsum_pallas_jit |
784.299 | 951.8 | 257.94 | 32.9% |
4.3 Performance Characteristics of Context Parallel
Measurement: TPU v6e-4 (cp_size=4, 4-core SPMD parallelism), bf16, single segment per_rank_T=8192 → global T=32768, H=16, K=V=128, BT=64; per-rank leading dim after chunking = 2128 = 16×133.
Single kernels (same padding basis):
| Kernel | Runtime (µs) | HBM (MB) | BW lower bound (µs) | BW utilization |
|---|---|---|---|---|
fused_recompute_w_u_vnew_from_h_pallas |
273.4 | 406.3 | 253.95 | 92.9% |
chunk_kda_bwd_dAv_kernel |
146.8 | 202.9 | 126.81 | 86.4% |
_fused_dhu_wy_intra_cumsum_pallas_jit |
872.7 | 915.1 | 571.97 | 65.5% |
CP chunk_gated_delta_rule_bwd_dhu_pre_process |
591.2 | 238.8 | 149.26 | 25.2% |
Measurement: TPU v7x-4 (cp_size=4, 4-core SPMD parallelism), bf16, single segment per_rank_T=8192 → global T=32768, H=16, K=V=128, BT=64; per-rank leading dim after chunking = 2128 = 16×133.
| Kernel | Runtime (µs) | HBM (MB) | BW lower bound (µs) | BW utilization |
|---|---|---|---|---|
fused_recompute_w_u_vnew_from_h_pallas |
181 | 406.3 | 110.11 | 60.8% |
chunk_kda_bwd_dAv_kernel |
100 | 202.9 | 54.99 | 55.0% |
_fused_dhu_wy_intra_cumsum_pallas_jit |
785 | 915.1 | 247.99 | 31.6% |
CP chunk_gated_delta_rule_bwd_dhu_pre_process |
511 | 238.8 | 64.72 | 12.7% |
The measured CP path used the same three core kernels as the non-CP path; its additional work came from the inter-rank state-gradient merge.
In the historical profile, the main CP cost was not the collective itself, but the local pre-process before communication:
- the pre-process needs to accumulate the input state gradient and the backward transition matrix along this rank's chunk sequence;
- this step has a scan/reduction nature and is hard to fully parallelize like an ordinary per-chunk matmul;
all_gatheritself is relatively cheap, because what is exchanged is the compressed state gradient and transition matrix, not the full-sequence token tensors.
Therefore, the optimization focus of the CP path should be on the pre-process:
- reduce its repeated reads of the full-chunk input;
- improve the parallelization of the chain product / scan;
- or move/fuse part of the state-merge logic forward into an existing backward stage.
4.4 Numerical Consistency
The current compute_intra_backward(...) implementation explicitly casts dg_acc + dg_intra to the input reference dtype and then back to fp32 before compute_reverse_cumsum_dg(...). This dtype boundary is part of the delivered numerical behavior and should be preserved or deliberately revalidated when the fusion is refactored. Comparisons against an unfused or XLA reference should use tolerances appropriate to this truncation point.
4.5 Revalidation Matrix for the Current Implementation
Because the measurements above are historical, current-head validation should cover correctness and performance separately. At minimum, correctness revalidation should exercise:
- the saved-
hpath (rematerialize_for_backward=False) and the full-rematerialization path (rematerialize_for_backward=True); - fixed-length and variable-length inputs, including multiple segments and
B>1; - a supplied
initial_state,output_final_state=True, and a nonzero final-state cotangent; - precomputed gates and
use_gate_in_kernel=True, with and withoutdelta_time_bias; use_qk_l2norm=True;- non-CP unaligned
K/Vshapes allowed by the backend contract; - CP execution at the supported 128-aligned
K/Vshapes and multiple CP sizes; - bf16 inputs and the supported fp32 path.
Performance revalidation should report the exact commit, TPU generation and topology, software versions, shapes, varlen distribution, CP size, warmup policy, iteration count, and whether each number is a per-device or end-to-end latency. Kernel-level bandwidth estimates should be accompanied by an end-to-end backward measurement so that launch, collective, and adapter overheads are visible.
5. Future Optimization Directions
The current implementation is already centered around reducing HBM round trips through kernel fusion, but the profiling results leave two clear follow-up directions.
5.1 Improve v7 Performance
In the historical measurements, the core kernels ran faster on v7x but achieved lower estimated bandwidth utilization than on v6e. A current profile should first confirm that result; if it remains true, follow-up work should focus on making the fused kernels better match v7's execution model:
- revisit tile sizes, mini-batch selection, and VMEM pressure for v7;
- reduce local layout overhead and padding traffic where it is clearly visible in profiles;
- identify which parts of the main fusion kernel are compute- or pipeline-bound rather than bandwidth-bound.
The goal is not to change the mathematical decomposition, but to retune the existing fused path so it scales better with v7's higher bandwidth and compute capability.
5.2 Optimize Context Parallel
The CP path adds a local pre-process before the cross-rank exchange. The historical profile suggests that this local scan/reduction work was a larger bottleneck than the collective communication itself; a current profile should confirm the balance before optimization work begins.
Future CP optimization should therefore prioritize:
- reducing repeated reads in the pre-process;
- improving the parallelization of the transition-matrix chain product;
- exploring whether part of the CP state-gradient preparation can be fused into the existing backward kernels.
The communication pattern should stay compact: exchange state-level summaries rather than full token-level tensors.