Skip to content

[8/8] perf(models): per-arch short-conv and KDA backward kernels for linear attention - #3634

Merged
Zhichenzzz merged 2 commits into
zhichen/kimi-k3-head-sharded-kdafrom
zhichen/delta-rule-conv-backend
Oct 8, 2026
Merged

Zhichenzzz merged 2 commits into
zhichen/kimi-k3-head-sharded-kdafrom
zhichen/delta-rule-conv-backend

Conversation

@Zhichenzzz

@Zhichenzzz Zhichenzzz commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

What

Two backend choices for the linear-attention layer, measured on GB300. Both stay within fla's own kernels.

  • Short conv: fla's mix backend (Triton forward, causal-conv1d CUDA backward) when causal-conv1d is installed; FLA_CONV_BACKEND overrides.
  • KDA backward on Blackwell: FLA_TILELANG=0 by default on compute capability ≥ 10, because fla's TileLang dqkg kernel is slower than its Triton one there. An explicit setting wins; Hopper keeps fla's default.

vs current main (GB300, 2026-10-08)

K3 KDA layer (hidden 7168, 96 x 128 heads), main's KimiK3Attention vs this stack's tip, forward + backward, SP when TP > 1:

TP 4k 16k 64k
1 17.20 -> 12.96 ms (1.33x) 47.68 -> 43.29 ms (1.10x) 192.65 -> 176.26 ms (1.09x)
2 12.65 -> 10.03 ms (1.26x) 28.29 -> 24.07 ms (1.18x) 110.16 -> 96.22 ms (1.14x)
4 11.13 -> 8.57 ms (1.30x) 18.60 -> 14.25 ms (1.31x) 70.60 -> 54.50 ms (1.30x)

This stack's tip includes this PR; see #3632 for the split.

Kernel measurements (GB300)

One short conv at Kimi-K3 shape (12288 channels), forward + backward:

Tokens Triton mix Triton vs float64 dx mix vs float64 dx
16k, 1 sequence 2.12 ms 1.17 ms
64k, 1 sequence 7.45 ms 5.27 ms 1.76e-3 1.66e-3
64k, 3 packed sequences 7.43 ms 4.31 ms

Outputs are identical. The float64 comparison was run on two packed sequences, 16k tokens in total.

chunk_kda forward + backward at Kimi-K3 shape (96 heads × 128):

Tokens TileLang, fla default (BK=BV=64, 4 warps) Best TileLang tiling (BK=BV=128, 8 warps) Triton
16k 21.5 ms 17.6 ms 17.0 ms
64k 77.2 ms 69.4 ms 66.9 ms

The Triton gradients match TileLang's to 6e-5 for g, and bitwise or within 1e-9 for q, k and v.

Layer measurements (GB300)

tests/manual/bench_linear_attn.py, head-sharded layer, forward + backward. The #3632 tip (99d3445db) and this branch (33bbef952) ran back to back on an otherwise idle node.

Layout Model 16k 64k 128k
TP=1 Qwen3.8-Flash GDN 8.40 → 7.64 ms (1.10x) 30.15 → 28.36 ms (1.06x) 59.70 → 56.47 ms (1.06x)
TP=1 GLM-5.3-Flash KDA 26.06 → 23.08 ms (1.13x) 103.70 → 94.35 ms (1.10x) 210.5 → 190.2 ms (1.11x)
TP=1 Kimi-K3 KDA 48.33 → 43.44 ms (1.11x) 200.6 → 181.0 ms (1.11x) 400.7 → 369.0 ms (1.09x)
TP=4 Qwen3.8-Flash GDN 5.72 → 5.32 ms (1.08x) 10.95 → 10.52 ms (1.04x) 21.17 → 20.24 ms (1.05x)
TP=4 GLM-5.3-Flash KDA 9.61 → 8.67 ms (1.11x) 33.66 → 32.36 ms (1.04x)* 68.70 → 63.83 ms (1.08x)
TP=4 Kimi-K3 KDA 15.55 → 14.35 ms (1.08x) 60.25 → 55.82 ms (1.08x) 119.8 → 117.2 ms (1.02x)
CP=4 Qwen3.8-Flash GDN –* 17.77 → 17.74 ms (1.00x) 34.75 → 34.35 ms (1.01x)
CP=4 GLM-5.3-Flash KDA 12.97 → 11.52 ms (1.13x) 34.49 → 33.22 ms (1.04x) 68.29 → 64.91 ms (1.05x)
CP=4 Kimi-K3 KDA 16.55 → 15.93 ms (1.04x) 59.98 → 57.80 ms (1.04x) 120.7 → 114.7 ms (1.05x)

* In the base run, two cells were outliers: GLM TP=4 64k at 155 ms and GDN CP=4 16k at 14.7 ms. For GLM TP=4 64k, the base value shown is from the preceding run with TileLang on. The GDN CP=4 16k cell has no clean base value and is left out.

  • Under CP=4, GDN gains nothing: its CP conv goes through fla's CP path and it has no TileLang kernel.
  • Peak memory is unchanged in every cell.

Against Kimi-K3's KDA layer before #3632, with identical weights and inputs (bench_k3_xcommit.py, forward + backward, parameter gradients compared after finalize_model_grads' TP sum):

TP 4k 16k 64k Peak memory at 64k Output / dx / worst dW rel err
1 12.78 → 11.52 ms (1.11x) 48.20 → 43.64 ms (1.10x) 195.8 → 177.9 ms (1.10x) 60.3 → 61.0 GiB bitwise / 3.7e-3 / 2.4e-3
2 9.91 → 8.21 ms (1.21x) 28.58 → 24.36 ms (1.17x) 111.4 → 96.5 ms (1.15x) 33.5 → 32.3 GiB bitwise / 5.3e-3 / 3.4e-3
4 10.29 → 7.25 ms (1.42x) 18.71 → 14.36 ms (1.30x) 70.8 → 54.8 ms (1.29x) 19.6 → 17.5 GiB 3.1e-3 / 6.0e-3 / 4.4e-3

Hopper (H200)

FLA_TILELANG is left at fla's default on Hopper because there, fla's TileLang dqkg is slightly faster than Triton:

chunk_kda forward + backward, Kimi-K3 shape 16k 64k
TileLang, fla default (BK=BV=64, 4 warps) 25.47 ms 102.38 ms
Triton 26.71 ms 106.08 ms
TileLang, BK=BV=128, 8 warps (not in this PR) 22.59 ms 89.86 ms

The last row is 12% faster than fla's default tiling on H200, with bitwise-equal gradients. That tiling is hard-coded inside fla, so it belongs upstream in fla rather than in miles.

The short conv on H200 at Kimi-K3 shape (12288 channels) goes from 9.53 ms (Triton) to 7.01 ms (mix, one sequence) and 5.86 ms (mix, three packed sequences) at 64k tokens, and from 2.43 ms to 1.55 ms at 16k.

Head-sharded layer, TP=1, forward + backward, #3632 tip → this branch:

Model 16k 64k
Qwen3.8-Flash GDN 13.47 → 13.03 ms (1.03x) 53.66 → 50.39 ms (1.06x)
GLM-5.3-Flash KDA 45.35 → 43.53 ms (1.04x) 185.0 → 180.9 ms (1.02x)
Kimi-K3 KDA 97.14 → 93.83 ms (1.04x) 385.3 → 379.1 ms (1.02x)

The Hopper numbers were measured with Megatron miles-main at 96fe16ecb.

Precision and tests (GB300, 33bbef952)

Test Result
test_linear_attn_short_conv.py (new float64 reference on Triton and mix, plus chunked vs single call) + tests/fast/models 32 passed
test_linear_attn_head_sharded.py: Qwen3.5 / Qwen3-Next / KDA vs replicated, TP=2 and TP=4 pass, pass
test_qwen3_5_cp_correctness.py, CP=2: GDN / KDA fwd 4.88e-4 / 1.81e-3, dx 2.37e-3 / 4.52e-3, pass
test_qwen3_5_cp_correctness.py, CP=4: GDN / KDA fwd 5.34e-4 / 1.66e-3, dx 2.41e-3 / 4.39e-3, pass

The CP tests compare the layer with CP against the same layer without it. The dx distance grew from about 1e-3 (GDN) and 3.7e-3 (KDA) because the two sides now run different conv backwards: mix without CP, fla's Triton CP conv with CP. Each side is the same distance from float64 as before.

Stack

PR Change
1 #3605 non-attention kernels → miles/kernels
2 #3606 attention kernels → miles/kernels/attention
3 #3607 one DSA kernel pair (GLM-5, DeepSeek-V4, V4.1)
4 #3608 per-arch DSA sparse attention dispatch
5 #3609 shared head-sharded linear-attention layer (miles_plugins/models/linear_attn.py)
6 #3610 Qwen3.5 / Qwen3-Next GDN on it
7 #3632 Kimi-K3 KDA on it
8 #3634 short-conv and KDA backward backends

Rules for miles/kernels are in its README: only kernels we write, one directory per op, names say what is computed (never the backend), callers import the entry, a torch-reference test per op.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 33bbef9 to e1f583e Compare September 23, 2026 08:43
@Zhichenzzz
Zhichenzzz added this pull request to stack #3611 September 23, 2026 18:18
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from e1f583e to 0cda557 Compare September 23, 2026 20:03
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 0cda557 to 388d7a9 Compare September 23, 2026 22:08
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 388d7a9 to 61ea268 Compare September 23, 2026 23:15
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch 2 times, most recently from 0de9f79 to ff6193f Compare September 24, 2026 04:58
@Zhichenzzz Zhichenzzz added run-ci-megatron run-ci-precision run-ci-low-precision run-ci-model-scripts Run model script smoke tests run-ci-miles-plugin Run CI tests labeled miles-plugin run-on-hopper Dispatch: run the selected tests on Hopper (H100/H200) only labels Sep 30, 2026
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 5173f58 to 507bd28 Compare October 8, 2026 00:03
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 507bd28 to 28ba0a8 Compare October 8, 2026 00:21
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 28ba0a8 to cbedd6d Compare October 8, 2026 02:17
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from cbedd6d to 17f641e Compare October 8, 2026 03:25
@Zhichenzzz Zhichenzzz changed the title [8/8] perf(models): faster short-conv and KDA backward for linear attention [8/8] perf(models): per-arch short-conv and KDA backward kernels for linear attention Oct 8, 2026
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 17f641e to 7a81d7f Compare October 8, 2026 07:51
@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

stage-c-8-gpu-b200 stopped at its first test, tests/e2e/megatron/model_scripts/test_deepseek_v4_1_4layer_ci.py (rank 0 finishes the optimizer step with CPU offload, the other ranks never reach the next DP / expert-DP collective, NCCL watchdog fires after 600 s), so the other 60 files in that lane never ran. The same test with the same sglang (b84bec5) and Megatron (fd15ee20a) passes on 8x H200 with this head, and every Hopper lane of this run is green; the B200-only hang is being reproduced on Blackwell separately.

Running the three files whose home suite is stage-c-8-gpu-b200 (they exercise the moved nvfp4 kernels) one by one below.

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

/rerun-test tests/fast-gpu/test_nvfp4_quantizer.py

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

/rerun-test tests/e2e/megatron/test_glm5_2_744b_a40b_5layer_nvfp4.py

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

/rerun-test tests/e2e/megatron/test_glm5_2_744b_a40b_5layer_nvfp4_w4a16.py

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

⚪ tests/fast-gpu/test_nvfp4_quantizer.py cancelled on stage-c-8-gpu-b200 in 2m41s — workflow run

The run was cancelled.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

⚪ tests/e2e/megatron/test_glm5_2_744b_a40b_5layer_nvfp4_w4a16.py cancelled on stage-c-8-gpu-b200 in 1m28s — workflow run

The run was cancelled.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

⚪ tests/e2e/megatron/test_glm5_2_744b_a40b_5layer_nvfp4.py cancelled on stage-c-8-gpu-b200 in 1m16s — workflow run

The run was cancelled.

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

/bypass-fastfail

@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 7a81d7f to 4dd7a68 Compare October 8, 2026 15:19
@radixark-miles-ci radixark-miles-ci Bot added the bypass-fastfail Disable CI fast-fail: run all stages and all tests to surface every failure label Oct 8, 2026
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/delta-rule-conv-backend branch from 4dd7a68 to 2e5d755 Compare October 8, 2026 16:47
@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

stage-c-8-gpu-b200: root-caused the test_deepseek_v4_1_4layer_ci.py hang on the B200 runner, and it is not from this stack.

  • With optimizer CPU offload, every rank's first HybridDeviceOptimizer.step() allocates its CPU fp32 grad buffers with pin_memory=True. On this host eight processes pinning ~150 GB each at once crawl at ~50 MB/s per process (py-spy: all 8 ranks in _set_sub_optimizer_grads, procs in D state). Rank 0 finishes in ~3 min, the other ranks need 20+ min, so rank 0's next DP / expert-DP collective trips the 600 s NCCL watchdog.
  • Reproduced on the runner host with main (43855d8) and with this stack: identical. The same test passes on 8x H200 (pinning is fast there).
  • Fix: 3600f9cb06 passes --no-pin-cpu-grads --no-pin-cpu-params to that test (test-only; the launcher default is untouched). With those flags the test passes on the same B200 host, all ranks, both rollouts.

bypass-fastfail stays on so this run also covers the other 60 files of the B200 lane.

…d backward

The head-sharded short conv used fla's Triton conv for forward and backward.
When causal-conv1d's CUDA backward imports (it does in the miles image), it
now uses fla's "mix" backend: Triton forward, causal-conv1d CUDA backward.
FLA_CONV_BACKEND overrides the choice, as it does for fla's own
ShortConvolution, and short_conv_backend logs it once. The CP path is
unaffected; fla routes it to its own CP conv whatever the backend.

GB300, one conv at Kimi-K3 shape (12288 channels), forward + backward:
64k tokens 7.45 -> 5.27 ms (one sequence) and 7.43 -> 4.31 ms (three packed
sequences); 16k 2.12 -> 1.17 ms. Against a float64 reference the mix
gradients are slightly closer than Triton's (dx 1.66e-3 vs 1.76e-3; outputs
are identical).

The conv test gains a float64 torch reference over packed sequences, run on
Triton and on the selected backend.
fla dispatches the fused KDA backward (chunk_kda_bwd_wy_dqkg_fused) to its
TileLang kernel whenever TileLang is installed. On GB300 that kernel is
slower than fla's own Triton implementation at every tiling fla offers; the
best TileLang config (BK=BV=128, 8 warps) still trails Triton. kda_kernel
now defaults FLA_TILELANG=0 on compute capability >= 10; an explicit
FLA_TILELANG still wins, and the value is logged once. Hopper keeps fla's
default until it is measured there. GDN has no TileLang backend.

GB300, chunk_kda forward + backward at Kimi-K3 shape (96 heads x 128):
64k tokens 77.2 -> 66.9 ms, 16k 21.5 -> 17.0 ms; gradients match the
TileLang path to 6e-5 (g) and bitwise-or-1e-9 (q, k, v).

Whole head-sharded layer, 64k tokens: Kimi-K3 KDA TP=1 194.8 -> 180.3 ms,
TP=4 58.3 -> 55.7 ms, CP=4 60.3 -> 57.7 ms; GLM-5.3-Flash KDA TP=1
99.9 -> 92.4 ms.

tests/fast/models/test_linear_attn_kernel_selection.py covers the
Blackwell default, Hopper, the explicit override and the FLA_CONV_BACKEND
override on CPU.
@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

Pushed two fixes at the bottom of the stack (rebased on main 07eb3ff817), both for bugs that are also on main:

  • f1ae62ca6e ci(tests): --no-pin-cpu-grads --no-pin-cpu-params for test_deepseek_v4_1_4layer_ci.py (the B200 optimizer-offload pinning hang above). In the previous run's B200 lane this test already ran with the flag and finished cleanly: all ranks completed both rollouts' train steps, no NCCL watchdog.
  • 97f53e1e97 fix(mbridge): the Qwen3.5 bridge indexed fused 3-D expert tensors with the EP-local expert id, so at EP>1 every rank above 0 loaded experts 0..n/ep-1. Qwen3.5-35B-A3B RL on 8x H200 (EP>1, direct HF load, same seed): before, raw_reward 0.0 for 38 rollouts with train/rollout logprob abs diff 0.033-0.057; after, reward 0.30 -> 0.82 by rollout 29 with logprob abs diff ~0.012.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bypass-fastfail Disable CI fast-fail: run all stages and all tests to surface every failure run-ci-low-precision run-ci-megatron run-ci-miles-plugin Run CI tests labeled miles-plugin run-ci-model-scripts Run model script smoke tests run-ci-precision run-on-blackwell Dispatch: move every selected test that supports Blackwell onto B200 run-on-hopper Dispatch: run the selected tests on Hopper (H100/H200) only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants