Skip to content

Add unordered hybrid kernels for transports without ordered delivery (opt-in via EP_HYBRID_KERNEL) - #732

Open
Xuan-1998 wants to merge 7 commits into
deepseek-ai:mainfrom
Xuan-1998:unordered-kernels
Open

Add unordered hybrid kernels for transports without ordered delivery (opt-in via EP_HYBRID_KERNEL) #732
Xuan-1998 wants to merge 7 commits into
deepseek-ai:mainfrom
Xuan-1998:unordered-kernels

Conversation

@Xuan-1998

Copy link
Copy Markdown

The hybrid (scale-out) dispatch/combine kernels synchronize through a trailing tail signal: the receiver assumes that when the tail arrives, all data written before it has landed. This holds on transports with ordered delivery and VA/strong signal support (e.g. IB RC), but not on unordered transports such as AWS EFA (SRD), it can be overcome with proxy thread but will hurt the performance. This PR adds an alternative kernel pair that only needs weak signals, so DeepEP's hybrid mode can run on those transports.

The default behavior is unchanged: EP_HYBRID_KERNEL defaults to ordered, which runs the existing kernels under the existing communication configuration, untouched and under their original file names. Setting EP_HYBRID_KERNEL=unordered selects the new pair.

Comment thread csrc/kernels/backend/nccl.cu Outdated
Comment thread csrc/elastic/buffer.hpp
Comment thread csrc/kernels/backend/nccl.cu Outdated
Comment thread csrc/kernels/backend/nccl.cu Outdated
Comment thread csrc/kernels/elastic/combine.hpp
@ds-review-bot

Copy link
Copy Markdown
Collaborator

🤖 ds-review-bot Code Review

v6

该补丁会破坏非 hybrid/direct 模式的默认 QP 与信号配置,并且部分合法 unordered 配置会因共享内存预算不足而无法启动 combine kernel。

v5

⚠️ 未完成评审(upstream_error:模型上游服务不可用)

v4p

本 MR 为无有序投递/无 VA 与强信号支持的传输(如 AWS EFA SRD)新增了 EP_HYBRID_KERNEL=unordered 的混合 dispatch/combine 内核对,通过分批 put、带内 header 和计数信号替代尾信号同步;默认仍为 ordered,使用原有内核。整体实现完整度较高,但 GIN 资源配置的分支重构影响了默认 ordered + allow_hybrid_mode=false 的直接模式路径,存在会导致运行失败或信号越界的回归;此外 unordered combine 在特定 channel/SM 组合下会超过线程上限,需修正。

Files reviewed: 19
Issues found: 🔴 2 critical | 🟡 3 warning
Inline comments posted: 5

⚠️ Parse warning: [v5] upstream_error:模型上游服务不可用

Xuan-1998 and others added 4 commits August 21, 2026 11:29
Infrastructure the unordered dispatch/combine kernels build on: a
unified GIN context/signal layout with per-peer barrier signal
indexing (gin_resource_alloc, qp_mapping), the NCCL device-comm setup
reconciled with upstream's runtime-version probe, and the elastic
buffer / Python plumbing that resolves the QP budget at construction.
One GIN context supplies one QP; the context count (default 11,
explicit via num_allocated_qps within [2, 17]) sets the per-context
indexed-signal budget that bounds the per-channel part count and the
ScaleOut rank count. Also the ptx/layout/comm helpers these paths
need.

Co-authored-by: Vladimir Aerov <vaerov@amazon.com>
Signed-off-by: Xuan Jiang <xuanj@amazon.com>
Split each channel's token stream into parts and sub-parts, each sent
as one batched put that carries an in-band header (iteration, token
count, continuation flag) and completes a per-part counting signal.
The receiver treats the signal purely as a completion count and
validates each batch through its header, so correctness holds no
matter what order the puts land in. Front-load a smaller first part to
cut time-to-first-forward, share one signal across a part's sub-parts
to stay inside the per-channel signal budget, and record a
per-(token, k) recv-slot map that combine later returns partials
through.

Co-authored-by: Vladimir Aerov <vaerov@amazon.com>
Signed-off-by: Xuan Jiang <xuanj@amazon.com>
Pack the scale-out return partials contiguously per channel and send
them as batched puts, each fused with a signal add on a shared
per-channel accumulator; the receiver gates on the accumulated count
instead of on arrival order. A dedicated proxy warp takes put issuance
off the data warps' critical path through a shared-memory hand-off
ring. The reduce epilogue locates partials through the per-(token, k)
recv map recorded at dispatch rather than assuming in-place token
slots. Carries upstream's expanded-send weight handling
(kDoExpandedSend) through the slot walk.

Co-authored-by: Vladimir Aerov <vaerov@amazon.com>
Signed-off-by: Xuan Jiang <xuanj@amazon.com>
The hybrid (scale-out) path now carries two kernel pairs, selected at
JIT-generation time via EP_HYBRID_KERNEL. The ordered pair (default)
is the existing implementation, kept untouched under its original
names (hybrid_dispatch.cuh / hybrid_combine.cuh) and running under the
existing communication configuration; it publishes a tail via a
trailing signal and requires the backend to support strong signals and
VA signals. The unordered pair (EP_HYBRID_KERNEL=unordered,
hybrid_dispatch_unordered.cuh / hybrid_combine_unordered.cuh)
synchronizes through in-band headers and counting signals, so strong
signal is not required, and weak signal is enough; use it on
transports without ordered delivery or VA/strong signal support
(e.g. EFA SRD).

The generated source names the variant's header and kernel, so the JIT
cache keys the two apart automatically. The combine reduce epilogue is
shared between the two paths via a kOrderedLayout template parameter.

Signed-off-by: Xuan Jiang <xuanj@amazon.com>
KeitaW and others added 2 commits August 24, 2026 18:02
On a single node `scaleout_active` is false, and the else arm copied
`num_allocated_qps` verbatim instead of calling `resolve_gin_context_cnt()`.
When the caller leaves that value at its default of 0 and the unordered hybrid
kernels are in use, the Python auto-fill is deliberately skipped so that C++
resolves the count, so a single-node run kept 0 all the way into the kernels:

  nccl.cu else arm                   -> gin_context_cnt = 0
  elastic.py get_theoretical_num_qps -> min(num_sms * 16 + 1, 0) = 0
  comm.cuh get_qp_mode               -> kNumQPs == 1 fast path skipped,
                                        kNumSMs <= kNumAvailableQPs false
  qp_mapping.cuh                     -> balanced_partition(idx, 512, 0) -> n / 0

Nothing rejects q == 0. Compiled for the host that specialization exits on
SIGFPE; in a GPU run it produced corrupted output and no reported fault: the
combine reported 623 MB in 1.148 us, and its output differed from the reference
in 99.98% of elements, surfacing as 'AssertionError: Diff: nan' in
tests/elastic/test_ep.py.

Resolve the count on both branches and write it back unconditionally, since
Python reads it via get_num_allocated_qps() and caps the per-launch QP count
with it. Add a static_assert so an illegal specialization fails to compile
rather than miscomputing, and assert the delegation invariant in Python at the
point the contract is broken.
An explicit num_allocated_qps outside the feasible GIN context range
used to fail the EP_HOST_ASSERT in resolve_gin_context_cnt. Clamp it
instead and warn, following the channels-per-SM budget reduction
warning in gin_resource_alloc.cuh:

- Unordered: clamped to [kMinGinContextCnt, kMaxGinContextCnt]
  (currently [2, 17]) in both the Python and C++ layers, each printing
  a [WARN] line with the requested and effective value. The Python
  clamp runs first, so the C++ warning only fires for direct C++ users.
- Ordered: passed through without restriction (matches upstream).
- 0: auto default (C++ resolves 11 for unordered; Python resolves
  65/129 for ordered hybrid, 17 for direct mode) which is unchanged.

The constants are exposed to Python as _C.min_unordered_gin_qps,
_C.max_unordered_gin_qps and _C.default_unordered_gin_qps.
Add a section on the ordered and unordered hybrid kernel pair selected
by EP_HYBRID_KERNEL: what each variant assumes about network delivery
order, which GIN signal types each requests, and why unordered is the
default and the only correct variant on backends that can deliver a
signal before the data it trails, such as the EFA GPU-initiated one.

Add a Running on AWS EFA section describing the two GIN backends
(EFA-GDA and CPU proxy) and the requirements: EFA installer 1.50.0 or
later, NCCL 2.31 or later for the GIN device API, GPUDirect RDMA (GDR)
support on both backend paths, and the gdrcopy kernel module, with an
explicit note that gdrdrv loading does not by itself confirm GDR
support. Link the EFA public guide and the awsome-distributed-ai
DeepEP V2 benchmark for the ready-to-run setup. Register
EP_HYBRID_KERNEL in the environment variable list.
@Xuan-1998 Xuan-1998 closed this Aug 29, 2026
@Xuan-1998
Xuan-1998 deleted the unordered-kernels branch August 29, 2026 21:01
@Xuan-1998
Xuan-1998 restored the unordered-kernels branch August 29, 2026 21:01
@Xuan-1998 Xuan-1998 reopened this Aug 29, 2026
@Xuan-1998 Xuan-1998 closed this Aug 29, 2026
@Xuan-1998
Xuan-1998 deleted the unordered-kernels branch August 29, 2026 21:02
@Xuan-1998
Xuan-1998 restored the unordered-kernels branch August 29, 2026 21:04
@Xuan-1998 Xuan-1998 reopened this Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants