Repository navigation
[Perf][FA4] Warm up dense (paged) FA4 attention kernels - #48156
sfc-gh-goliaro wants to merge 3 commits into
Conversation
The CuTeDSL warmup only covers the FA4 MLA prefill path. Standard GQA models on the FLASH_ATTN backend (FlashAttentionForwardSm100) therefore JIT-compile the dense prefill specialization (q_stage=2, paged, seqused_k) on the first serving request, causing a large latency spike and, in single-pass throughput benchmarks, a ~3x apparent gap vs FlashInfer. Register the dense FA4 backend (FlashAttentionMetadataBuilder) as a CuTeDSL warmup provider (new fa4_dense_config.py) and precompile the dense decode and prefill specializations up front, mirroring the MLA-prefill provider. Restricted to the vLLM-compile path (CompilationMode.VLLM_COMPILE) where query/paged-KV tensors have the contiguous/paged strides the compiled kernel bakes; eager falls back to stock in-serving JIT unchanged. Requires the paged compile-spec support in vllm-flash-attention (compile_flash_attn_varlen_func_from_specs: seqused_k/page_table/k_stride/descale) and a corresponding flash-attn pin bump. Results (Llama-3.1-8B, B200, 1024/1024, 256 prompts): 0 in-serving CuTeDSL compiles; FA4 19.9k -> 54.9k tok/s (single-pass gap 3.0x -> ~1.12x vs FlashInfer); greedy output byte-identical to FlashInfer. Also verified on Qwen3-VL-8B-FP8. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Gabriele Oliaro <gabriele.oliaro@snowflake.com>
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging. To run CI, PR reviewers can either: Add If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
LopezCastroRoberto
left a comment
There was a problem hiding this comment.
Thanks for extending the FA4 warmup coverage. Great work, @sfc-gh-goliaro!
Left some comments.
|
|
||
| FA4_DENSE_STANDARD_DTYPES = (torch.bfloat16, torch.float16) | ||
|
|
||
| # A prefill-sized query (rows > q tile) selects the q_stage=2 kernel; a |
There was a problem hiding this comment.
Since you’ve identified the lowest-level compile keys here already, could we use q_stage instead of higher-level variables such as prefill size? e.g., for FA4 MLA
In general, I think we should identify the atomic factors that determine different compile keys, and it looks like you've already identified most of them. I would use the atomic ones whenever possible.
There was a problem hiding this comment.
Done. Now driven by the atomic factor: q_stage=2 via max_seqlen_q = tile_m + 1 (SM100 tile 128), q_stage=1 via max_seqlen_q=1. This is the same pattern as the MLA config.
| # single-token-per-seq query selects q_stage=1. Concrete magnitudes below the | ||
| # tile boundary do not change the compiled kernel, only the q_stage selection. | ||
| _PREFILL_Q_ROWS = 256 | ||
| _DECODE_BATCH = 8 |
There was a problem hiding this comment.
Same thing for this one, i.e., ideally we should try to find the atomic factors that trigger a different compile-key. Probably batch size is now the fundamental factor?
There was a problem hiding this comment.
Probably batch size is now the fundamental factor?
Hmm not batch, batch isn't in compile_key. The atomic decode factor is is_split_kv (num_splits>1); batch only flips it through FA4's split heuristic, and serving passes num_splits=0. Now enumerated directly via num_splits {1,2} , which also adds the split-KV decode kernel the old num_splits=1-only version missed.
|
|
||
|
|
||
| def _supports_fa4_dense(ctx: FA4DenseCompileContext) -> bool: | ||
| major, _ = torch.cuda.get_device_capability() |
There was a problem hiding this comment.
Ideally, this should use current_platform, not torch.cuda directly. See https://github.com/vllm-project/vllm/pull/46182/changes#r3501268810
There was a problem hiding this comment.
Done! now using current_platform.get_device_capability()
| and ctx.head_dim > 0 | ||
| and ctx.page_size > 0 | ||
| and ctx.max_blocks_per_seq > 0 | ||
| and major == 10 |
There was a problem hiding this comment.
What about SM90 and SM120? What are the compile-keys in that case?
There was a problem hiding this comment.
Added an arch-family helper. Per the FA4 selector: q_stage=2 exists only on SM100/SM110 (SM90 & SM120 are always q_stage=1); split-KV is rejected on SM120; fp8 is SM100-only. So we emit: SM90 {q1}x{split,no-split}, SM100/110 {q1,q2}x{split,no-split}, SM120 {q1}. Unknown arches skip warmup (fall back to JIT).
|
This pull request has merge conflicts that must be resolved before it can be |
…orm, SM90/SM120
Rework the dense FA4 CuTeDSL warmup to enumerate the kernel's atomic
compile-key factors (q_stage, is_split_kv) directly instead of prefill-size /
decode-batch proxies, mirroring the MLA-prefill config. Use current_platform
for the device capability, and cover all FA4 arch families via an arch-family
helper: SM100/SM110 emit q_stage in {1,2}; SM90 and SM120 are q_stage=1 only;
SM120 omits split-KV (rejected by the kernel).
Drop the fp8/descale warmup branch: the FA4 fp8 kernel writes a bf16 output but
compile_flash_attn_varlen_func_from_specs hardcodes the compile-only output
dtype to q_dtype (no out_dtype), so an fp8-in/bf16-out spec cannot be built.
fp8-KV dense serving falls back to in-serving JIT until that helper gains an
out_dtype. Remove the now-unused has_fp8_kv context field.
Validated on B200 (SM100): all four bf16 dense specs JIT-compile and select the
intended q_stage/is_split_kv/paged/seqused_k kernels.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Gabriele Oliaro <gabriele.oliaro@snowflake.com>
# Conflicts: # cmake/external_projects/vllm_flash_attn.cmake
|
Merged the changes from main and addressed @LopezCastroRoberto's comments: the warmup now enumerates atomic compile_key factors ( Dropped the fp8/descale branch: the FA4 fp8 kernel writes a bf16 output, but Validated on B200 (SM100): all 4 bf16 dense specs JIT-compile and select the intended Still draft as cmake pin stays at the vllm-project/flash-attention#158 commit until it lands in main. |
|
This pull request has merge conflicts that must be resolved before it can be |
There was a problem hiding this comment.
Thanks a lot for doing this, @sfc-gh-goliaro!
Would you mind moving this to the warmup contract we're trying to establish? See: #47451. Specifically vllm/v1/attention/backends/mla/prefill/flash_attn.py. RFC here: #47456
I think it should be very straightforward in your case, since you identified the core compile-keys already.
|
This pull request has been automatically marked as stale because it has not had any activity within 90 days. It will be automatically closed if no further activity occurs within 30 days. Leave a comment if you feel this pull request should remain open. Thank you! |
[Perf][FA4] Warm up dense (paged) FA4 attention kernels
Repo:
vllm-project/vllm· Branch:fa4-dense-cutedsl-warmupSummary
The CuTeDSL warmup path (
register_cutedsl_warmup_provider) currently only warms theFA4 MLA prefill kernel. Standard GQA models on the
FLASH_ATTNbackend(
FlashAttentionForwardSm100) therefore JIT-compile the dense prefill specialization(
q_stage=2, paged,seqused_k) on the first serving request — a large one-time latencyspike (~8 s) that
jit_monitorexplicitly flags asCuTeDSL JIT compilation during inference: FlashAttentionForwardSm100.This registers the dense FA4 backend (
FlashAttentionMetadataBuilder) as a CuTeDSL warmupprovider (new
vllm/model_executor/warmup/fa4_dense_config.py) and precompiles the densedecode and prefill specializations at startup, mirroring the existing MLA-prefill provider
in
mla/prefill/flash_attn.py.CompilationMode.VLLM_COMPILE), where query andpaged-KV tensors have the contiguous/paged strides the compiled kernel bakes. Under
enforce_eager, tensors can be strided views, so warmup is skipped and eager falls backto stock in-serving JIT (unchanged).
Dependency (why this is a draft)
Requires the paged compile-spec support in
vllm-flash-attention:vllm-project/flash-attention#158 (
compile_flash_attn_varlen_func_from_specs:seqused_k/page_table/k_stride/descale). The cmakeGIT_TAGhere points at thatPR's commit; it must be updated to the merged flash-attn commit before this can build
in CI. Kept as a draft until #158 lands.
Not a duplicate
to FA4 MLA prefill only. Neither touches the dense backend
(
vllm/v1/attention/backends/flash_attn.py) nor warmsFlashAttentionForwardSm100forstandard GQA models. This builds on the merged [Feat][1/N] CuTeDSL warmup infrastructure, FA4 MLA #46182 provider registry and fills that gap.
dense fix. Searched open PRs/issues for dense-FA4 /
FlashAttentionForwardSm100warmup —none found.
Tests run + results
Hardware: 1× B200 (SM100), vLLM built from this branch. Throughput via
vllm bench throughput --attention-backend FLASH_ATTN --dataset-name random --input-len 1024 --output-len 1024 --num-prompts 256, cold CuTeDSL cache,VLLM_PLUGINS="".jit_monitor: 0CuTeDSL JIT compilation during inferenceduring serving (was 1, thedense prefill kernel).
--enforce-eagerruns to completion (rc=0), warmup skipped — no regression.Model evaluation (output correctness)
Greedy generation (
temperature=0) withFLASH_ATTNvsFLASHINFERon Llama-3.1-8B isbyte-identical across test prompts (e.g. "17 × 23" → "391", primes → "2, 3, 5, 7, 11").
Warmup only pre-populates the same
compile_keythe engine would JIT, so numerics areunchanged.
Residual warmed gap — out of scope for this PR (kernel, not warmup)
After cold-start is eliminated, a ~12% gap remains on decode-heavy workloads. Isolated
attention-kernel microbenchmarks (FA4 vs FlashInfer trtllm, identical paged inputs, outputs
validated to match, CUDA-event timed) attribute it entirely to the FA4 decode kernel;
prefill is at parity or faster:
CUDA graphs (which vLLM already uses for FA4 decode as FULL graphs) eliminate FA4's per-call
host overhead, so at serving batch sizes these pure-kernel ratios drive the end-to-end gap.
Closing it would require FA4 decode-kernel work (a dedicated
q_stage=1kernel / bettersplit-KV vs FlashInfer's trtllm-gen decode cubin) — a separate effort, not this PR.
Reviewer notes
ruff check/ruff formatclean on the changed files; runpre-commitbefore merging.intentionally not addressed here.
🤖 AI-assisted change; a human must review every line and run the tests/evals before submitting.