fix(kernel): order the batch-memcpy probe against the current stream - #414
fix(kernel): order the batch-memcpy probe against the current stream#414tspeaks wants to merge 1 commit into
Conversation
_probe allocates its destination with torch.zeros, which enqueues the fill on the current stream, then enqueues the verification copy on a fresh probe stream. Nothing joins the two. When the current stream has a backlog the copy completes first on the independent probe stream and the fill lands on top of it, so the probe reads back zeros and load_batch_memcpy raises "cudaMemcpyBatchAsync probe copied wrong bytes" on a GPU that supports the API. OffloadMoeCache catches that and falls back to full-layer copies, so --moe-prefill-hit-d2d silently does nothing whenever the current stream is busy as the probe runs -- in practice during prefill warmup, which is exactly when the flag is first exercised. A cold process hides the bug: the first torch.zeros pays a cudaMalloc and the first torch.cuda.Stream() populates the per-device stream pool, and each of those synchronizes the device, draining the backlog before the copy is enqueued. That is why the probe passes when run standalone and fails inside a warmed-up server. Join the probe stream to the current stream before the copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Reproduced, and it is biting a production deployment — so this is worth more than a tidy-up. Box: 2 x RTX 6000 Ada (sm_89), TP=2, Qwen3.8-Flash-Next NVFP4, offload MoE backend, torch 2.11.0+cu130, CUDA 13. Server runs with I ran
Eight queued matmuls is enough. Your explanation of why it looks fine standalone matches exactly: at backlog 0 and 1 it passes here too, because the first Why it matters beyond the probe. Two suggestions, neither blocking:
Taking the one-line fix onto our deploy branch now. 🤖 Generated with Claude Code |
_probe allocates its destination with torch.zeros, which enqueues the fill on the current stream, then enqueues the verification copy on a fresh probe stream. Nothing joins the two. When the current stream has a backlog the copy completes first on the independent probe stream and the fill lands on top of it, so the probe reads back zeros and load_batch_memcpy raises "cudaMemcpyBatchAsync probe copied wrong bytes" on a GPU that supports the API.
OffloadMoeCache catches that and falls back to full-layer copies, so --moe-prefill-hit-d2d silently does nothing whenever the current stream is busy as the probe runs -- in practice during prefill warmup, which is exactly when the flag is first exercised.
A cold process hides the bug: the first torch.zeros pays a cudaMalloc and the first torch.cuda.Stream() populates the per-device stream pool, and each of those synchronizes the device, draining the backlog before the copy is enqueued. That is why the probe passes when run standalone and fails inside a warmed-up server.
Join the probe stream to the current stream before the copy.