Skip to content

Commit 5cdd2d3

Browse files
committed
feat(deepseek-v4): let inference.sh launch across nodes
examples/models/deepseek_v4/inference.sh always ran torch.distributed.run --nproc_per_node=$((PP * EP)), i.e. all ranks on one node. With the DeepSeek-V4-Pro defaults (PP=4, EP=8) that is 32 processes on a single node, which a 4-GPU GB200 NVL72 node cannot run although the header says the defaults are for GB200. Add NNODES / NODE_RANK / MASTER_ADDR / MASTER_PORT (and GPUS_PER_NODE) knobs. When NNODES > 1 the launcher passes --nnodes/--node_rank/--master_addr/ --master_port to torch.distributed.run; PP*EP must be divisible by NNODES. With the variables unset the behaviour is unchanged. Document the Slurm form (one task per node) in the README. Verified on 8 x GB200 (TP1/PP4/EP8, real DeepSeek-V4-Pro weights from an imported Megatron checkpoint): coherent generation, 12 min 49 s including the 3 TB load. Signed-off-by: linmuchuiyang <6169320+linmuchuiyang@users.noreply.github.com>
1 parent 2e09c23 commit 5cdd2d3

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

‎examples/models/deepseek_v4/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ required storage and validate checkpoint resume explicitly.
222222
[`inference.sh`](inference.sh) example uses legacy full-prefix generation and
223223
is not a verified KV-cache path. HF-native inference is the target after a
224224
verified Megatron-to-HF export.
225+
- [`inference.sh`](inference.sh) needs `PP * EP` GPUs (32 for the Pro
226+
defaults `PP=4`, `EP=8`). On 4-GPU nodes such as GB200 NVL72 spread them over
227+
several nodes with one task per node:
228+
`srun --ntasks-per-node=1 bash -c 'NNODES=$SLURM_JOB_NUM_NODES NODE_RANK=$SLURM_NODEID MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n1) ./inference.sh'`.
229+
With the variables unset the script launches all ranks on the current node.
225230
- Fused mHC requires Blackwell (`sm_100`). Hopper uses the unfused path.
226231
- CPU import and export require enough RAM for the full BF16 model plus
227232
conversion workspace and remain unverified in the current card.

‎examples/models/deepseek_v4/inference.sh‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
# EP: expert-parallel size (default: 4 for Flash, 8 for Pro)
2323
# PP: pipeline-parallel size (default: 1 for Flash, 4 for Pro)
2424
# PROMPT: prompt string (default: "Explain hyper-connections in transformer models.")
25+
# NNODES / NODE_RANK / MASTER_ADDR / MASTER_PORT: multi-node launch (defaults 1 / 0 / 127.0.0.1 / 29500).
26+
# The PP*EP ranks are spread over NNODES nodes; GPUS_PER_NODE defaults to PP*EP/NNODES.
27+
# Under Slurm run one task per node, for example:
28+
# srun --ntasks-per-node=1 bash -c 'NNODES=$SLURM_JOB_NUM_NODES NODE_RANK=$SLURM_NODEID \
29+
# MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n1) ./inference.sh'
2530
# NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN: optional HybridEP topology override
2631
# NVLINK_DOMAIN_SIZE: optional physical NVLink-domain size override
2732
# USE_MNNVL: optional HybridEP fabric override (0 or 1)
@@ -51,9 +56,26 @@ if [[ -z "${PP:-}" ]]; then
5156
fi
5257
TP=1
5358

59+
# Multi-node launch: PP*EP ranks over NNODES nodes (default: everything on this node).
60+
NNODES=${NNODES:-1}
61+
NODE_RANK=${NODE_RANK:-0}
62+
MASTER_ADDR=${MASTER_ADDR:-127.0.0.1}
63+
MASTER_PORT=${MASTER_PORT:-29500}
64+
WORLD=$((PP * EP))
65+
if (( WORLD % NNODES != 0 )); then
66+
echo "ERROR: PP*EP=${WORLD} is not divisible by NNODES=${NNODES}" >&2
67+
exit 1
68+
fi
69+
GPUS_PER_NODE=${GPUS_PER_NODE:-$((WORLD / NNODES))}
70+
TORCHRUN_ARGS=(--nproc_per_node="${GPUS_PER_NODE}")
71+
if (( NNODES > 1 )); then
72+
TORCHRUN_ARGS+=(--nnodes="${NNODES}" --node_rank="${NODE_RANK}"
73+
--master_addr="${MASTER_ADDR}" --master_port="${MASTER_PORT}")
74+
fi
75+
5476
# DSv4 hybrid attention does not support inference contexts yet, so use full-prefix generation.
5577
# Inference directly from the HF checkpoint (Bridge dequantises in-flight).
56-
uv run python -m torch.distributed.run --nproc_per_node=$((PP * EP)) \
78+
uv run python -m torch.distributed.run "${TORCHRUN_ARGS[@]}" \
5779
examples/conversion/hf_to_megatron_generate_text.py \
5880
--hf_model_path "${HF_MODEL_ID}" \
5981
--prompt "${PROMPT}" \
@@ -65,7 +87,7 @@ uv run python -m torch.distributed.run --nproc_per_node=$((PP * EP)) \
6587
# Inference from a previously-imported Megatron checkpoint (faster cold start).
6688
MEGATRON_DIR="${WORKSPACE}/models/${MODEL_VARIANT}"
6789
if [[ -d "${MEGATRON_DIR}/iter_0000000" ]]; then
68-
uv run python -m torch.distributed.run --nproc_per_node=$((PP * EP)) \
90+
uv run python -m torch.distributed.run "${TORCHRUN_ARGS[@]}" \
6991
examples/conversion/hf_to_megatron_generate_text.py \
7092
--hf_model_path "${HF_MODEL_ID}" \
7193
--megatron_model_path "${MEGATRON_DIR}" \

0 commit comments

Comments
 (0)