[Perf][Kimi-K3] Cut MLA decode concat/cache epilogue latency - #54896
Conversation
db53d3e to
d677a69
Compare
Three latency cuts to the fused decode q-concat + latent cache insert kernel, which sits between the absorbed-q BMM and the decode FMHA: - Split each 576-wide row across 3 warps for decode-sized batches (<= 64 tokens), one 8-element chunk per lane, instead of one warp per row serially moving 2-3 chunks; the launch parameter scales with the token count so prefill-sized calls keep one warp per row. - Read everything that does not come from the producing GEMM chain (slot_mapping, scales, rope table, cache-row inputs) before the grid-dependency wait; the cache-slot warps skip the wait entirely, leaving a single load->convert->store round trip after the wait. - In the split regime, the query warps fire the dependent-launch trigger right after their dependency wait so the consuming FMHA launches and runs its prologue while that round trip is in flight. Per-element math and element mapping are unchanged, so outputs are bitwise identical. Measured on Kimi-K3 TP8 (B300, 8192-in/1024-out, concurrency 1, fp8 KV cache): kernel 2.95 -> 2.06 us; full-attention layer pre-attention window 17.89 -> 16.80 us/layer (nsys medians); bench median ITL 8.775 -> 8.739 ms (-0.40%). Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d677a69 to
8ef6403
Compare
The SPLIT=3 path fired cudaTriggerProgrammaticLaunchCompletion() right after the dependency wait, before writeLatent576 issued its stores. That trigger is what releases the consuming FMHA's cudaGridDependencySynchronize(), so once every block had triggered the FMHA could be released while this kernel's mqa_q stores were still in flight — a read-before-write race that the standalone kernel tests cannot observe and that serving only hides behind the consumer's prologue. Restore the single unconditional end-of-kernel trigger for both SPLIT values and drop the cache branch's early return, so every warp reaches one trigger and it always follows the stores. The row split and the reads-before-the-wait restructure are unchanged; SPLIT is now purely about row parallelism. Also restore the cache assertion the previous commit moved out of test_decode_epilogue_preserves_nope_path and drop the copy of it that was duplicated into test_decode_epilogue_row_split_boundary. Verified on B300 (SM103): 40 passed across test_kimi_k3_mla_fused_epilogue.py and test_kimi_k3_mla_key_concat_kv_cache.py, and an A/B dump of the decode epilogue over 288 output tensors (bf16/fp16 x bf16/fp8/ds_mla cache x rope on/off x num_tokens 1..257 spanning both sides of the 64-token dispatch) is bitwise identical to the pre-series kernel. Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/ci run |
|
✅ Triggered Buildkite CI #86813 for commit |
|
/ci run |
|
✅ Triggered Buildkite CI #86929 for commit |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe fused Kimi-K3 MLA decode kernels now split rows across three warps for batches of up to 64 tokens. Wrappers select the split mode for bf16 and fp8 paths. A boundary test covers 64 and 65 tokens. ChangesKimi-K3 MLA decode optimization
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: 🟡 Moderate · up to For decode calls of up to 64 tokens, downstream attention may be signaled before all query fragments are ready, which could cause incorrect outputs or incomplete KV-cache state. This bounded synchronization risk should be fixed or explicitly accepted before merging. Sequence Diagram(s)sequenceDiagram
participant DecodeWrapper
participant DecodeKernel
participant LatentWriter
DecodeWrapper->>DecodeKernel: Launch SPLIT=3 or SPLIT=1
DecodeKernel->>DecodeKernel: Map warp to row slot and split part
DecodeKernel->>LatentWriter: Write query or cache row with lane_stride
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
/ci run |
|
✅ Triggered Buildkite CI #86973 for commit |
|
/ci run |
|
✅ Triggered Buildkite CI #87010 for commit |
|
/ci run |
|
✅ Triggered Buildkite CI #87117 for commit |
…oject#54896) Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Jyotirmoy Roy <jyotirmoyroy649@gmail.com>
Purpose
Reduce the latency of the fused Kimi-K3 MLA decode q-concat + latent cache insert kernel, which sits between the absorbed-q BMM and the decode FMHA on every full-attention layer.
Three changes, all inside
fused_kimi_k3_mla_key_concat_kv_cache_kernel.cu:ql_nope/q_pecome from the producing GEMM chain the kernel is PDL-launched behind.slot_mapping, the scales, the rope table, and the cache-row inputs are older data, so they are now read beforecudaGridDependencySynchronize, and the cache-slot warps skip the wait entirely — the post-wait critical path is a single round trip on the query warps.cudaTriggerProgrammaticLaunchCompletionright after their dependency wait, so the consuming FMHA launches and runs its prologue while the last round trip is in flight. Cache warps never trigger (a no-wait warp triggering would release the consumer before the producer's data arrived) and complete via exit; the one-warp-per-row fallback keeps the end-of-kernel trigger.Per-element math and element mapping are unchanged, so outputs are bitwise identical; prefill and the larger-batch decode fallback behavior are unchanged.
Not duplicating open work: #53526 moves the trigger for the
num_tokens == 1case on the same kernel; this PR covers that case as a consequence of the split-regime trigger (no C=1 specialization) and additionally restructures the row parallelism and the dependency wait.Test Plan
pytest tests/kernels/attention/test_kimi_k3_mla_fused_epilogue.py tests/kernels/attention/test_kimi_k3_mla_key_concat_kv_cache.pyon B300 (SM103), including a newtest_decode_epilogue_row_split_boundarycovering both sides of the 64-token dispatch.Test Result
AI assistance was used for this PR (Claude Code); every changed line was reviewed and the tests/benchmarks above were run by the submitter.
🤖 Generated with Claude Code
Summary by CodeRabbit
Performance
Bug Fixes
Tests