[Bugfix][Attention] Size FlashInfer paged-KV buffers for local attention - #50022
thegoldenflow wants to merge 3 commits into
Conversation
|
GPU verification is done. Ran the FlashInfer chunked-local test on an RTX 4090: 4 passed with this patch. Reverting only the two source files this PR touches reproduces #49980 with the predicted "provided out is the wrong size for the accumulation", and restoring them returns to 4 passed. Neighbouring flashinfer tests show no regressions, and full pre-commit passes on Linux. Both the GPU test and Linting sections of the body now carry the real output, so this is no longer a draft. |
|
I confirm that cherry-picking this patch into the current upstream vLLM |
|
This pull request has merge conflicts that must be resolved before it can be |
FlashInferMetadataBuilder preallocates paged_kv_indptr, paged_kv_last_page_len and paged_kv_indices from scheduler_config.max_num_seqs. Under chunked local attention, make_local_attention_virtual_batches replaces the batch with one virtual batch per local attention block and reports that count as num_reqs, which is decoupled from max_num_seqs. A prefill longer than attention_chunk_size therefore overflows the buffers, raising "provided out is the wrong size for the accumulation" from the cumsum in _compute_flashinfer_kv_metadata, and silently truncating the shared indptr view on the trtllm-gen prefill path, which never reaches that cumsum. Size the buffers from an upper bound on the virtual batch count instead. The builder already receives attention_chunk_size on its kv_cache_spec, on ChunkedLocalAttentionSpec and on the FullAttentionSpec it is promoted to when the hybrid KV cache manager is disabled, so no new plumbing is needed. The page count only ever grows: when the hybrid manager is disabled, all layers promote to FullAttentionSpec and merge into a single KV cache group whose spec keeps attention_chunk_size (FullAttentionSpec.merge asserts only over fields(AttentionSpec), which excludes it). The global attention layers form their own attention group but share that spec, and they attend over the whole sequence, so capping pages per request at the chunk size would under-size their buffer. Growing on overflow is not a general option here: _get_decode_wrapper hands these buffers to CUDA-graph decode wrappers as fixed-address buffers and fast_decode_plan does not copy into them, so rebinding them would leave those wrappers reading stale storage. Keep a guarded backstop that raises instead of reallocating when decode CUDA graphs are enabled. Closes vllm-project#49980 Signed-off-by: Jason Yao <wsyjh8@gmail.com>
Signed-off-by: Jason Yao <wsyjh8@gmail.com>
_get_decode_mask caches the uniform XQA speculative-decode mask with max_num_seqs rows and returns its first num_decodes rows. Chunked local attention can split a verify window evenly across a chunk boundary, so the uniform decode count can exceed max_num_seqs; the slice then comes back short and XQA, which indexes the mask per request without a shape check, reads past its end. Size the cached mask from max_buffer_reqs like the paged-KV buffers, drop cached masks when those buffers grow, and remove the now-unused max_num_reqs attribute. Signed-off-by: Jason Yao <wsyjh8@gmail.com>
d4dd5cb to
05cc6b7
Compare
|
@janbernloehr Thank you for verifying this on DGX H100 with your Llama-4 Scout workload, much appreciated! I've since rebased onto current main and referenced your result in the PR description. |
Purpose
Fixes #49980.
Under chunked local attention,
make_local_attention_virtual_batchesturnsNrequests intoM > Nvirtual batches and passesMto the builder asnum_reqs.FlashInferMetadataBuildersizedpaged_kv_indptr,paged_kv_last_page_lenandpaged_kv_indicesfrommax_num_seqs, so they overflow wheneverM > max_num_seqs. The reporter (max_num_seqs=1) hitValueError: provided out is the wrong size for the accumulationfrom thenp.cumsumin_compute_flashinfer_kv_metadata.This PR sizes those buffers in
__init__from an upper bound on the virtual batch count,min(2 * max_num_seqs + cdiv(max_num_batched_tokens, c), max_num_batched_tokens)(new helpermax_local_attention_virtual_batches).getattr(spec, "attention_chunk_size", None). The builder gets aChunkedLocalAttentionSpecwhen the hybrid KV cache manager is on (the CUDA default). When it is off (e.g. EAGLE,--disable-hybrid-kv-cache-manager) it gets aFullAttentionSpec(attention_chunk_size=...), which anisinstancecheck would miss. The test covers both.build()skips_compute_flashinfer_kv_metadataand runs atorch.cumsuminto an out-of-range slice ofpaged_kv_indptr.gpu. The only signal is a warning, and the kernel gets acum_seq_lens_kvthat is too short. The issue's grow-on-overflow check lives in_compute_flashinfer_kv_metadata, so it would never see this path. Rebinding the buffers is also unsafe: CUDA-graph decode wrappers alias them, andfast_decode_plandoes not copy into them._ensure_paged_kv_capacityruns at the top ofbuild()and again once the page count is known. If the bound is ever wrong, it grows the buffers, or raises whenenable_cuda_graphis set.max, notmin). With the hybrid manager off, all Llama-4 layers merge into one KV cache group whoseFullAttentionSpeckeepsattention_chunk_size. The global-attention layers read that spec but attend over the whole sequence, so capping pages at the chunk size would under-size them (2250 → 512 pages in the reporter's config)._decode_mask_cache, also sized frommax_num_seqs. With XQA speculative decoding, a verify window split evenly by a chunk boundary can produce more uniform decodes than that. XQA indexes the mask per request without a shape check, so it reads past the end. The cache now uses the same bound. Reaching this needs Llama-4 with FlashInfer on SM90/SM12x and speculative decoding (any method) with an oddk >= 3.Why the bound holds, and what it allocates
With
c = attention_chunk_size,N = max_num_seqsandT = max_num_batched_tokens, each request contributes1 + cdiv(q_i - f_i, c)virtual batches, wheref_i = min(c - ((s_i - q_i) mod c), q_i) >= 1. Summing givesV <= 2N + cdiv(T, c). The2Nterm is needed:q_i = 2withf_i = 1givesV = 2N. Every virtual batch holds at least one query token, soV <= T. Every virtual batch sees at mostcKV tokens, so pages<= V * cdiv(c, block_size). A brute-force check over 2.7M configurations found no violations. Zero-length padded requests only occur in FULL CUDA-graph mode, which chunked-local rules out (AttentionCGSupport.NEVER).paged_kv_indptrpaged_kv_indicesmax_model_len=36000,T=36000,N=1,c=8192, block 16)max_model_len=131072,T=8192,N=256,c=8192, block 16attention_chunk_sizeQuestion for reviewers:
FullAttentionSpec.mergekeepsattention_chunk_size, but its consistency assert only checksfields(AttentionSpec), which excludes it. Is that intended? If not, themax()above can go. I left it as out of scope.Test Plan
To see the failures, revert
vllm/v1/attention/backends/flashinfer.pytoorigin/mainand re-run the second file.create_vllm_configfetchesconfig.jsonfrom the gatedmeta-llama/Meta-Llama-3-8B, so an HF token is needed.Test Result
main, CPU only. The only conflict was [MRV2] Buffer util simplifications #56888's buffer-type refactor; the fix was ported with its logic unchanged.test_chunked_local_attention.py: 11 passed.test_flashinfer_chunked_local_attention.py: 5 passed, on a copy with the CUDA-only skip removed and a locally cached Qwen config. Withflashinfer.pyreverted toorigin/main: 3 failed. Both paged-KV cases fail with the [Bug]: FlashInfer builder ValueError 'provided out is the wrong size for the accumulation' with Llama-4 chunked local attention when a prefill exceeds attention_chunk_size and max_num_seqs is small #49980ValueError, and the mask case fails withassert 1 == 2.ValueError.pytest tests/v1/attention/ -k flashinfer: 13 passed, 844 skipped.0.26.1rc1.dev528+gf8d03e774, passes their previously fatal 32k-prefix Llama-4 Scout scenario on DGX H100 (ISL avg 34,000.80 tok, 108.23 output tok/s). Thank you! It covers only the [Bug]: FlashInfer builder ValueError 'provided out is the wrong size for the accumulation' with Llama-4 chunked local attention when a prefill exceeds attention_chunk_size and max_num_seqs is small #49980 native-prefill path: that build predates [MRV2] Buffer util simplifications #56888, MRV2 becoming the default and SM90 XQA decode.pre-commit(including manual-stage mypy 3.10–3.13) passes on Windows, exceptupdate-dockerfile-graph, which needs/bin/bash.AGENTS.md