mirror of
https://github.com/deepseek-ai/DeepEP.git
synced 2026-11-05 00:47:32 +00:00
[GH-ISSUE #722] Legacy normal kernels trap for hidden >= 8192: TMA staging buffer caps hidden with no host-side check #409
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
deepseek-ai/DeepEP#409
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @yashkgp on GitHub (Aug 12, 2026).
Original GitHub issue: https://github.com/deepseek-ai/DeepEP/issues/722
Summary
The legacy "normal" (high-throughput)
dispatch/combinekernels stage each token through afixed-size per-warp TMA buffer in shared memory. The buffer size is a hard-coded
constexpr, but thestaged payload grows with
hidden, sohiddenis silently capped. Exceeding the cap is caught onlyby a device-side assertion, which
traps inside the kernel.As a result
hidden = 8192cannot run on the normal path at all, even though the low-latencykernels explicitly instantiate it (
case 8192inSWITCH_HIDDEN,csrc/kernels/legacy/launch.cuh).Affected sites
hidden(num_topk=8)csrc/kernels/legacy/intranode.cu:304(dispatch)hidden_int4 / 2 * 16 + 8 <= 8192csrc/kernels/legacy/internode.cu:578(dispatch forwarder/receiver)num_bytes_per_token + 8 <= 16384csrc/kernels/legacy/internode.cu:1816(combine sender)num_bytes_per_token + 8 <= 16384The combine site is also hit by FP8 dispatch, because combine is always BF16.
intranodecombineandinternodecombine's forwarder path are not affected: their budgets arechecked with
EP_STATIC_ASSERTagainst hidden-independent quantities.Why it fails badly
EP_DEVICE_ASSERT(deep_ep/include/deep_ep/common/exception.cuh:41) isprintf+asm("trap;")with no
NDEBUGescape, and there is no host-side check for these quantities anywhere. So thefailure is not a Python-level error; it is an in-kernel trap that surfaces as an asynchronous
CUDA error: an illegal instruction was encountered, frequently reported against an unrelated latercall. The only clue is a
printffrom the device.Reproduction
Any BF16
dispatchwithhidden >= 8192reproduces it, including the backward pass of a model withhidden = 8192(Llama-3-70B, Qwen2-72B), where dispatch of gradients is BF16 even if the forwarddispatch is FP8.
Suggested fix
hiddenshould not be bounded by an internal staging buffer. Concretely:intranode.cudispatch can be madehidden-agnostic cheaply: it already splits each tokeninto 2 TMA chunks, so it only needs to split into as many chunks as the buffer can hold.
internode.cucombine sender has free headroom: the forwarder warps already size thiskernel's dynamic shared memory (
9248 * 24 = 221952 B), while the senders only claim16384 * 8 = 131072 B. Deriving the sender budget from the forwarder budget raises its cap withthe total dynamic shared memory unchanged.
internode.cudispatch stages a whole token (data +SourceMeta+ scales + top-k) in oneTMA, so lifting its cap needs either chunking or a larger budget (
16384 -> 24576would growdynamic shared memory from 128KB to 217KB and shrink L1, so it wants a benchmark).
At minimum, these limits should be checked on the host so they raise an actionable exception
instead of trapping in the kernel.
PR with 1, 2 and host-side checks for 3: yashkgp/DeepEP#fix/legacy-normal-tma-hidden-limit
Note on verification
The caps above were derived from the source and checked exhaustively with a script, not on hardware
— I do not currently have an NVIDIA GPU to run the repro on. Please treat the exact numbers as
"derived from the formulas in the two files" rather than measured.