mirror of
https://github.com/deepseek-ai/DeepEP.git
synced 2026-11-05 00:47:32 +00:00
[GH-ISSUE #596] detect_accessible_ranks returns incorrect NVLink domain size on GB200 NVL72 (MNNVL) #364
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#364
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 @samnordmann on GitHub (Mar 31, 2026).
Original GitHub issue: https://github.com/deepseek-ai/DeepEP/issues/596
Environment
hybrid-epNVSHMEM_DISABLE_MNNVL=0(MNNVL enabled)Problem
On GB200 NVL72 systems with MNNVL enabled,
ExtendedMemoryAllocator.detect_accessible_ranks()over-counts the NVLink domain size because GPU memory on remote nodes is accessible via MNNVL NVLink. This causesHybridEPBufferto setnum_of_ranks_per_nodeto the full EP group size (e.g., 8 or 32) instead of the physical node size (4), andnum_of_nodesto 1.Consequence: The
device_sync_kernelbarrier expects all EP ranks in a single NVLink domain. Since the dispatch/combine kernels are launched with a grid sized for the physical node, the barrier deadlocks waiting for atomic increments that never arrive.Root Cause
Two compounding factors:
1. NVML reports
FFFFFFFF:FF:FF.0for all NVLink peersOn GB200 NVL72, all NVLink connections go through an NVSwitch fabric (MNNVL). NVML's
nvmlDeviceGetNvLinkRemotePciInfocannot resolve the PCI bus ID of peers behind the switch and returns the sentinel valueFFFFFFFF:FF:FF.0for every link — both local-node and remote-node peers are indistinguishable:topo_detection.cuhhandles this correctly by classifying unknown peers as NVSwitch (0x068000). However, this means the XML-based topology detection provides no information about which GPUs are on the same physical node vs. remote nodes.2. IPC accessibility test succeeds across nodes
detect_accessible_ranks()inallocator.cuusescuMemSetAccess/ IPC memory handles to test which ranks can access the current GPU's memory. With MNNVL enabled (NVSHMEM_DISABLE_MNNVL=0), GPU memory IS accessible across nodes via NVLink. The test returnsnum_accessible_ranks == group_sizeinstead ofgpus_per_physical_node.The resulting misconfiguration:
Reproduction
Reproducer script (
probe_detect_ranks.py):Actual output on GB200 NVL72 (2 nodes, 4 GPUs/node, 8 ranks total,
NVSHMEM_DISABLE_MNNVL=0):Expected:
detect_accessible_ranks=4(physical GPUs per node).Actual:
detect_accessible_ranks=8(full group size — cross-node GPUs are IPC-accessible via MNNVL).The issue does not manifest when
num_of_ranks_per_nodehappens to equal the group size (e.g., TP=4 DP=4 → 4 EP ranks on 4 GPUs/node). It only manifests when TP < GPUS_PER_NODE, creating multiple EP ranks per node.Workaround
The existing env var override works correctly:
This bypasses
detect_accessible_ranks()entirely (hybrid_ep_buffer.py:64-66).Suggested Fix
detect_accessible_ranks()tests memory accessibility, but on MNNVL systems this is not equivalent to being on the same physical node. The detection should additionally check whether peers share the same host (e.g., viaboot_idor hostname comparison, astopo_detection.cuhalready does withgetHostHash()). Ranks that are memory-accessible but on different hosts should not be counted as part of the NVLink domain.Alternatively,
detect_accessible_rankscould incorporate the topology information from the XML/topo detection pass (which correctly identifies per-host system IDs viahost_hash) to bound the domain to same-host ranks only.@Autumn1998 commented on GitHub (Apr 16, 2026):
Hi @samnordmann ,
Thank you very much for your kind correction. However, in Hybrid EP, the term “nodes” here (in num_of_nodes) actually refers to NVL domains. In other words, num_of_nodes really means the number of NVL domains. There are some historical reasons for this naming, so it can indeed be a bit confusing.
Our intention is to enable communication within each NVL domain, and the detection pattern you pointed out is exactly what we need. If NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN=4 is used, communication that should originally go over NVLink would instead fall back to RDMA, which would increase communication time.
The grid size in dispatch and combine should only be related to performance optimization. As long as we pass the correct parameter information during JIT compilation, a hang should not occur. If a hang does occur, then we should address the hang itself.
@samnordmann commented on GitHub (Apr 16, 2026):
Got it, thanks for explaining!