Repository navigation
[Bugfix][ROCm] Stage pageable H2D copies in async_copy_to_gpu through pinned memory - #1403
hadihonarvar wants to merge 1 commit into
Conversation
… pinned memory On ROCm (RX 7900 XTX, gfx1100) with Model Runner V2, the non_blocking copy of query_start_loc from a pageable numpy array intermittently leaves the first elements of input_buffers.query_start_loc stale, even after a full device synchronize. Attention then reads the KV cache at wrong offsets and Qwen3 answers garbage from the first or second request on. Stage pageable input through the caching host allocator before the non_blocking copy, which is what upstream main does since vllm-project#56888, where async_copy_to_gpu was folded into async_tensor_h2d. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Hadi Honarvar <hadi@opod.io>
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels. Just a reminder: PRs would not trigger full CI run by default. Instead, it would only run You ask your reviewers to trigger select CI tests on top of 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. 🚀 |
…worker image On the vLLM 0.23 RDNA base, Model Runner V2 copies query_start_loc to the GPU with a non_blocking copy from pageable memory; on ROCm that copy intermittently does not land and Qwen3 answers garbage after the first request. stage-pageable-h2d.py applies upstream's fix (vllm-project/vllm#56888; ROCm/vllm#1403 for the RDNA release) at build time where the old code is present, and does nothing on any other vLLM. Measured on an RX 7900 XTX: 0/12 correct before, 12/12 after. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Overview
On Radeon RDNA3 (RX 7900 XTX, gfx1100), the
rocm/vllm:rocm7.14.1_rdna_ubuntu24.04_py3.14_pytorch_2.11_vllm_0.23.0image answers garbage for Qwen3 models from the first or second request onward. The cause is onenon_blockinghost-to-device copy from pageable memory inasync_copy_to_gpu. This PR stages that copy through pinned memory, which is what upstreammainhas done since vllm-project#56888. That change landed two weeks after this branch was cut.Claims
async_copy_to_gpu(model_runner.py×3,pp_utils.py,structured_outputs.py).Validation
Repro. Run on the published image above, unmodified, with one RX 7900 XTX:
': 1.0.0.0\n: 1.0.0.1…', then'1111111111…'×5'Paris, Seine.''Paris, Seine.'gpu_memory_utilization=0.88), 2 runs' of of of …''Paris. Seine.''Paris\nSeine''Paris\nSeine'(unchanged)CI coverage. No CI job runs gfx1100, so this PR adds no test. All validation above was run by hand on an RX 7900 XTX (ROCm 7.14.1, torch 2.11.0+rocm7.14.1, HIP 7.14.60850).
ruff checkandruff formatpass on the changed file.Details
How the copy was found (each step on the same card and image)
--enforce-eager), the custom RMSNorm/RoPE ops, switchingROCM_ATTN↔TRITON_ATTN, fp16↔bf16, and hipBLASLt↔rocBLAS changed nothing.AMD_SERIALIZE_KERNEL=3,HIP_LAUNCH_BLOCKING=1andAMD_SERIALIZE_COPY=3each make it 4/4 correct. Serializing only the copies is enough.non_blockingcopy to blocking fixes it; forcing only device-to-host copies does not; forcing only host-to-device copies does.model_runner.py:879,async_copy_to_gpu(query_start_loc_np, out=self.input_buffers.query_start_loc), fixes it.query_start_loc_npis a fresh pageablenp.empty.outright after each copy, and again aftertorch.cuda.synchronize(), shows 5 to 14 copies per request at that site where elements 1..15 of the 257 hold stale zeros instead of29(one 29-token request). They are wrong in both snapshots, so the data never lands; it is not merely late. The host source is unchanged afterwards. Every other copy is correct.Root cause, as far as it is isolated.
async_copy_to_gpuissuesout.copy_(x, non_blocking=True)straight from pageable memory. The comment it replaces says pinning was removed on purpose. On this ROCm stack, that copy intermittently fails to deliver its first bytes while the model runner overlaps steps. I could not reproduce the loss in a standalone PyTorch loop (pageable or pinned sources, default or side stream, 16 B to 256 KiB, 0/64-byte alignment, a concurrent device-to-host copy on another stream, 1/3/8 copies back to back: all 0 failures). So the exact HIP-side mechanism is not isolated here and may be worth a separate look by the HIP team. Within vLLM the failing operation is pinned down to this one copy.The fix. If the source is pageable, copy it into a pinned tensor (
torch.empty(..., pin_memory=True).copy_(x)), then issue thenon_blockingcopy. The staging tensor comes from PyTorch's caching host allocator, which keeps it alive until the copy completes. Upstream replaced this function withasync_tensor_h2din vllm-project#56888, and that does exactly this. The removed comment worried about "sporadic stalls … due to CUDA driver contention" from explicitpin_memory(). This path does not callcudaHostAllocper copy: it reuses blocks from the caching allocator, which is whatmainships. The added host work is one memcpy of at mostmax_num_reqs + 1int32 per step.Why a backport rather than a rebuild. The RDNA images for v0.23.0 come from this branch, and vLLM v0.30.0+ already contains the fix. Either merging this, or publishing an RDNA image built from a release that contains vllm-project#56888, resolves it. Tracking issue: #1404.
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.
Contact: Hadi Honarvar, hadi@opod.io or hadi.work.ca@gmail.com (https://opod.io)