[Perf][Attention] Pin MLA chunked-context metadata tensors so H2D copies are truly non-blocking - #45074
Conversation
…ies are truly non-blocking Co-authored-by: Claude Signed-off-by: zixi-qi <zixi@inferact.ai>
ivanium
left a comment
There was a problem hiding this comment.
👍 Makes sense to me. Do we need to fix the DCP path too? cc @GirasoleY @njhill
There was a problem hiding this comment.
Should we apply the same fix to DCP path too?
…etadata build Co-authored-by: Claude Signed-off-by: zixi-qi <zixi@inferact.ai>
TBH the benchmark setting is a bit biased in favor of this change, in a realistic compute bound prefill only eager run the improvement should be much smaller. But I left the existing benchmark numbers to demonstrate that the fix has taken effect |
WoosukKwon
left a comment
There was a problem hiding this comment.
Holding since I found it confusing
Thanks @WoosukKwon for the review! The changed code is actually not invoked by DeepSeek V4 so initial benchmark result are most likely due to performance variance from running decode in eager mode. Reran the benchmark with Kimi + prefill only workload and updated the PR description |
WoosukKwon
left a comment
There was a problem hiding this comment.
Thanks for the update!
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…adata + unbounded SSM state write Two distinct out-of-bounds faults on hybrid GDN models (Qwen3.5/3.6) on RDNA3 TP2, both surfacing as "Memory access fault ... Page not present". 1) Prefill (root cause, proven): the FLA chunk metadata (chunk_indices/chunk_offsets) was async-copied to the GPU with .to(non_blocking=True) from non-pinned (pageable) host memory. With a truly-async runtime (torch 2.10 on ROCm) the copy may not land before the chunk_gated_delta_rule_fwd_h kernel reads it, so the kernel sees uninitialised GPU memory as chunk_offsets -> garbage `boh` -> OOB write. Captured: both TP ranks read *different* garbage (~4.3e18) for a 33-tok prefill while cu_seqlens was correct. Fix: pin the source tensors so the non_blocking H2D copy is safe (same fix as MLA vllm-project#45074). 2) Decode (hard backstop): the in-place SSM state write in fused_sigmoid_gating guarded only the NULL_BLOCK_ID=0 lower bound, so a stale/out-of-range slot index would write hundreds of MB past the state cache and page-fault. Add an upper-bound check (idx < num_state_slots); skipping an out-of-range slot is recoverable, an OOB write is not. Also keeps the chunk_delta_h OOB diagnostic guard (cheap length check + opt-in VLLM_GDN_DEBUG_OOB value check) as a backstop, and ignores the local many-FAILS/ debug log dir. Signed-off-by: JartX <sagformas@epdcenter.es>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai> Signed-off-by: divineearthly <divineearthly@gmail.com>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
…ies are truly non-blocking (vllm-project#45074) Signed-off-by: zixi-qi <zixi@inferact.ai>
Purpose
Fix a hidden host-side stall in the MLA chunked-prefill metadata build that serializes CPU metadata prep with GPU execution on every scheduler step.
In
MLACommonMetadataBuilder.build(), the chunked-context section creates three small CPU tensors and ships them to the GPU with.to(device, non_blocking=True)when assemblingChunkedContextMetadata:cu_seq_lens_cpu— allocated withpin_memory=True✅chunk_starts— pageable ❌token_to_seq_tensor_cpu— pageable ❌non_blocking=Trueis only actually asynchronous when the host source is pinned. From pageable memory, CUDA makes the copy host-synchronous: the CPU blocks insidecudaMemcpyAsyncuntil the GPU stream has drained all previously launched kernels. During chunked-prefill serving of long prompts this happens on the hot path of every step, so the metadata build stalls until the previous step's forward pass finishes.Measured on Kimi-K2.5-NVFP4 (GB200 ×4,
--enforce-eager,--max-num-batched-tokens 4096, 80k-token prompts, concurrency 16): the metadata build took 67–101 ms per 4096-token step — the single largest per-step cost — while the actual tensor work in that section is microseconds (1 prefill, 1–2 context chunks). Section timers confirmed ~100% of it was the chunked-context block, i.e. the hidden stream drain.The fix pins the remaining pageable tensors so the H2D copies are truly asynchronous (
.pin_memory()onchunk_starts,pin_memory=Trueontoken_to_seq_tensor_cpu), and — per review feedback — the same fix on the DCP path (.pin_memory()onlocal_chunk_starts, the only pageablenon_blocking=Truesource there;padded_local_cu_chunk_seq_lens_cpuwas already pinned). No behavior change.Duplicate check: searched open PRs for
pin_memory,token_to_seq,chunk_starts,chunked prefill pinned,non_blocking pageable— no open PR addresses this (closest hits are unrelated: #44149 is LoRA adapter device detection, #34393 is MLA gather-kernel unification).Test Plan
A/B benchmark on a 4× GB200 node, identical server and workload, only the pin change differing. Kimi-K2.5 uses standard MLA, so long-context chunked prefill goes through the fixed
ChunkedContextMetadatabuild. Output length is 1 so the measurement isolates prefill:With
--max-num-batched-tokens 4096and 32k-token prompts, every prefill step after the first runs the chunked-context metadata build, exercising the fixed path.Lint:
ruff check/ruff format --checkpass on the changed file.Test Result
48/48 requests succeeded in both runs (1.57M input tokens each, prefill-only):
At 384 prefill steps per run (48 × 32k / 4096), the −3.0 s duration delta is ~8 ms saved per step in this configuration; the per-step stall grows with context length (the original 80k-prompt measurement showed 67–101 ms metadata builds), and in mixed prefill+decode serving the same stall additionally shows up as decode ITL tail latency, so end-to-end gains there are larger.
This PR was developed with AI assistance (Claude Code); the changed lines and benchmark results have been reviewed by the human submitter.