Replies: 1 comment
|
This is an excellent debugging write-up. The pattern you're seeing—TP within a node works, TP across nodes hangs in NCCL LL protocol wait loops—is a classic symptom of rank synchronization drift in multi-node tensor parallelism, not a fabric-level failure. Root Cause AnalysisThe key evidence:
This pattern typically occurs when:
Diagnostic Approach1. Enable NCCL Collective Tracing with OpCountexport NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=COLL,GRAPH
export NCCL_DEBUG_FILE=/tmp/nccl_rank_%r.logThis will log each collective's 2. Check for CUDA Graph Bucket BoundariesSGLang uses CUDA graphs to batch collective calls. The hang correlation with generation length suggests you're hitting a bucket boundary where:
Test: Disable CUDA graphs for collectives: export SGLANG_DISABLE_CUDA_GRAPH=1If hangs stop, the issue is CUDA graph replay desynchronization. 3. Monitor Per-Rank Scheduler StateAdd logging to SGLang's scheduler to track which batch each rank is processing: # In sglang/srt/managers/scheduler.py, before collective calls:
logger.info(f"Rank {self.tp_rank} processing batch_size={len(batch)}, step={self.step}")If ranks show different batch sizes or step counts before a hang, the scheduler is allowing state divergence. 4. Check MoE Routing (if applicable)For DeepSeek V4 Flash/Pro with MoE layers: export NCCL_ALGO=NVLS # Try NVLink SHARP if available
export CUDA_DEVICE_MAX_CONNECTIONS=8 # Increase connection poolMoE routing can cause different ranks to enter different allgather patterns if expert assignments diverge. Likely Fix: Enforce Collective SynchronizationThe root cause is probably that SGLang's scheduler allows ranks to progress through decode steps independently, and over many iterations, small timing differences accumulate until one rank enters a collective while another is still processing the previous step. Workaround 1: Reduce watchdog timeout # In your launch config
--watchdog-timeout 60 # Force earlier detectionThis won't fix the drift but will fail faster, preventing indefinite hangs. Workaround 2: Force synchronous scheduler steps # Before each collective in the model forward pass:
torch.distributed.barrier(group=self.tp_group)Workaround 3: Use PP instead of TP across nodes python -m sglang.launch_server \
--tp 4 --pp 4 \
--dp 1This keeps tensor parallelism within nodes (where it's fast and reliable) and uses pipeline parallelism across nodes (which is more tolerant of latency). Fabric-Level DiagnosticsIf the above doesn't resolve it, the next step is to capture libfabric traffic: export FI_LOG_PROV=cxi
export FI_LOG_LEVEL=debug
export FI_CXI_DISABLE_HOST_REGISTER=1Then use cxi_dump -t -n 0 # Dump all CXI traffic on node 0Look for:
Questions for Further Debugging
SummaryThis is almost certainly scheduler-level rank desynchronization amplified by CUDA graph replay, not a fabric bug. The LL protocol wait loops are behaving correctly—they're just waiting for data that will never arrive because the peer rank is on a different collective sequence. Start with
Your debugging is spot-on—this is exactly the right approach for narrowing down distributed systems issues. |
Uh oh!
There was an error while loading. Please reload this page.
When running SGLang with tensor parallelism spanning multiple nodes (confirmed on both TP16/4-node and TP8/2-node), long-running generations eventually trigger the scheduler watchdog timeout and hang. CUDA coredump analysis shows the GPU is parked in NCCL's normal LL-protocol wait loop (polling for a peer's data that never arrives), not a kernel fault. The same setup works reliably when TP is confined to a single node (e.g. TP4+PP4 on 16 GPUs / 4 nodes never hangs).
This looks like it could be a fabric-layer issue, an SGLang/NCCL rank-synchronization issue that only manifests over inter-node collectives, or a combination - filing here for guidance on how to narrow it down further and whether this pattern is familiar to anyone.
Environment
libfabricCXI provider)Symptom
SGLang's scheduler watchdog fires after the configured hard timeout:
This sends SIGQUIT to the process, which (with CUDA coredump-on-signal enabled) produces a GPU coredump.
Correlation with generation length: hangs appear to correlate with longer generations (more decode steps). Short queries/responses do not seem to trigger it; the failure rate seems to increase with the number of decode iterations, consistent with a rare per-collective-call event rather than a deterministic trigger - though a structural cause (e.g. CUDA graph bucket crossing, MoE routing imbalance) has not been ruled out.
Coredump analysis
Two independent hangs analyzed via
cuda-gdb target cudacore:Hang 1 -
ncclDevKernel_AllReduce_Sum_bf16_RING_LLActive(not an exception/fault state)Hang 2 -
ncclDevKernel_AllGather_RING_LLDisassembly at both PCs (shared pattern across both hangs)
This is NCCL's standard LL-protocol wait primitive: poll the receive-buffer flag for a peer's data, with a spin-count-gated periodic check of
comm->abortFlag. Seeing the identical wait-loop shape in both AllReduce and AllGather suggests this isn't a bug in either collective's kernel code specifically - both are waiting correctly for data that never arrived within the watchdog window. Same issue is present if I setNCCL_ALGO=Treeso this is not RING specific issue.What's been ruled out / narrowed down
Active, and the disassembly shows a legitimate (if endless) wait loop, not a trap.NCCL_NET_GDR_LEVELmakes no difference; hang reproduces identically with GDR on or off. This argues against a GDR-specific data path issue (memory registration, BAR1 mapping, ODP) and toward something common to both paths.FI_LOG_PROV=cxi FI_LOG_LEVEL=warncame back clean during the actual hang window No libfabric-detected error during the hang itself so far.What we're asking for
RING_LL)? Any known issues withaws-ofi-ncclor CXI provider on this generation of hardware/interconnect?NCCL_DEBUG_SUBSYS=COLLatINFOlevel?Happy to share both coredumps, full NCCL INIT/NET/COLL logs, and our
standalone repro script if useful.
All reactions