Repository navigation
[Perf][FlashAttention] FA4: pass no FP8 descale for a unit per-tensor scale - #63232
Conversation
There was a problem hiding this comment.
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.
…ear for M <= 16 On gfx950, RocmDotScaledMxfp8LinearKernel quantized every activation with a separate mxfp8_e4m3_quantize launch before the tl.dot_scaled GEMM. For bf16 activations with 1-16 rows, the GEMM now loads the bf16 tile and quantizes it in registers with the same fp32 operations, so each linear is one launch and the output is bit-identical. Every N-tile requantizes its activation, so the in-kernel work grows with K while the saved launch does not. With BLOCK_K >= 256 the kernel quantizes only min(16, next_pow2(M)) rows and repeats them to fill the 16-row MFMA tile (rows >= M are never stored); at BLOCK_K=128 repeating the rows costs more than it saves. On MI355X this fuses K <= 1024 for M <= 16 (1.14-1.64x) and K=2048 for M <= 8 (1.06-1.26x on the MiniMax-M3 TP=4 o_proj, 0.89x at M=16); larger K keeps the separate quant. K becomes a constexpr, which the fused loop needs to be profitable (the same change is in vllm-project#63232); each power-of-2 row count is one more specialization, compiled during warmup. fp16 is not fused, because the in-kernel fp8 cast enables the FP16 overflow clamp for the whole kernel. VLLM_BATCH_INVARIANT keeps the unfused path. Adds unit tests and benchmarks/kernels/benchmark_rocm_mxfp8_linear_fused_quant.py. Signed-off-by: RuibinCheung <ruibzhan@amd.com>
…ear for M <= 16 On gfx950, RocmDotScaledMxfp8LinearKernel quantized every activation with a separate mxfp8_e4m3_quantize launch before the tl.dot_scaled GEMM. For bf16 activations with 1-16 rows, the GEMM now loads the bf16 tile and quantizes it in registers with the same fp32 operations, so each linear is one launch and the output is bit-identical. Every N-tile requantizes its activation, so the in-kernel work grows with K while the saved launch does not. With BLOCK_K >= 256 the kernel quantizes only min(16, next_pow2(M)) rows and repeats them to fill the 16-row MFMA tile (rows >= M are never stored); at BLOCK_K=128 repeating the rows costs more than it saves. On MI355X this fuses K <= 1024 for M <= 16 (1.14-1.64x) and K=2048 for M <= 4 (1.03-1.27x); past M=4 the K=2048 gain does not hold for every N, and larger K keeps the separate quant. K becomes a constexpr, which the fused loop needs to be profitable (the same change is in vllm-project#63232); each power-of-2 row count is one more specialization, compiled during warmup. fp16 is not fused, because the in-kernel fp8 cast enables the FP16 overflow clamp for the whole kernel. VLLM_BATCH_INVARIANT keeps the unfused path. Adds unit tests and benchmarks/kernels/benchmark_rocm_mxfp8_linear_fused_quant.py. Signed-off-by: RuibinCheung <ruibzhan@amd.com>
|
@zyongye when can we expect it to land? |
|
This pull request has merge conflicts that must be resolved before it can be |
|
FYI @zyongye: #64264 is a small ROCm follow-up stacked on this branch. With NVFP4 KV on MI355X, the FlyDSL kernels compile lazily on first launch, and |
… scale FA4 reads a missing descale as 1.0, and it copies every expanded descale view into a contiguous tensor: three copy kernels per layer and step. The scales are fixed once the weights are loaded, so a per-tensor scale of 1.0 (an FP8 KV cache without calibrated scales) passes no descale. Calibrated and per-head scales keep theirs; a (1, 1) expanded view gets canonical strides, because FA4 treats it as contiguous and its layout check then rejects the zero stride. Co-authored-by: qizixi <22851944+zixi-qi@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
|
/ci run |
|
✅ Triggered Buildkite CI #98868 for commit |
Overview
For an FP8 KV cache whose per-tensor scales are 1.0, FlashAttention now passes FA4 no descale at all. Before, it passed broadcast descale views that FA4 copied into contiguous tensors, three copy kernels per attention layer and step.
This PR began as the squash of the whole MiniMax-M3.1-Flash tree. That squash has since been split into the PRs below; this PR now holds only the FA4 change.
How #63232 was split
AttentionSpec.kv_head_major_slots)UniformTypeKVCacheSpecsgroupsis_paddingfrom the positions kernel, top-p fixmain'smax_num_new_slots_for_draftingalready returns 0 for DFlash/DSparkmainvia #63270The full squash as it was before the split is a0ea941 (on 5d35ce3), in this PR's force-push history.
PRs that were opened against this branch (
minimax-m31-flash) while it held the squash: #64359, #64273, #64264, #64206, #63528, #63491 and #62509. Their diffs against this branch now include the whole old squash (e.g. #64359 shows 193 files). The model (#64111) and the ROCm re-quantization (#64378) are onmain, so they can retargetmain.Claims
FlashAttentionImplpassesNonefor that descale.maybe_contiguousno longer copies the three broadcast descale views per layer and step.Validation
On GB300:
148 passed, 9 skipped, 4 failed. The 4 failures are
test_sliding_window_encoder_backend_correctness(embeddinggemma-300m). They fail the same way onmain(2abde62) in this environment.The new
test_flash_attn_fa4_unit_fp8_descale_is_exact(Blackwell only) checks that:Not covered:
Details
_scale_floatmirrors decide the case on the host with no GPU sync. A per-head scale's float mirror holds only its maximum, so the check also requires a single-element scale.flash_attn.py. None of them changes howFlashAttentionImplpasses descales.Credits: qizixi wrote the FA4 change and is a co-author in the commit. AI assistance (Claude) was used to split the original squash into the PRs above, to write the FA4 test and to draft this description. The submitter reviewed the change and ran the tests above.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code