Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions training/slurm/quickstart/launch_training.sbatch
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/bin/bash

#SBATCH --nodes 1
#SBATCH --ntasks-per-node 8
#SBATCH --gpus-per-node 8
Comment on lines +4 to +5

@sangstar sangstar Mar 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be true for all jobs with GPUs. Some have less than this allocatable.

Also, this is a personal preference thing but I highly prefer the setup --ntasks-per-node=1 --gpus-per-task=8 whenever possible. One task running the sbatch before fanning out in the training script allows you to be able to install things and modify any other filesystem state without running in to race conditions.

The only change required for this to work is to simply wrap the entrypoint script in torchrun, which also has the added benefit of automatically assigning the correct identifying environment variables to each rank, like:

torchrun \
  --nproc_per_node="$SLURM_GPUS_PER_NODE" \
  --nnodes="$SLURM_NNODES" \
  --node_rank=$SLURM_NODEID \
  --master_addr="$MASTER_ADDR" \
  --master_port="$MASTER_PORT" \
  pretrain_gpt.py "${train_args[@]}"

@dmarx dmarx Mar 31, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a great call out and suggestion, thanks. I'm also not a huge fan of hardcoding this to 1 node. An alternative could be removing all three of those directives from the top entirely and instead modeling usage to the user with CLI args, i.e.

sbatch launch_training.sbatch \
  --nodes 1 \
  --ntasks-per-node 1 \
  --gpus-per-task=8

thoughts?

#SBATCH --constraint gpu
#SBATCH --job-name test
#SBATCH --output test.%j
#SBATCH --export all

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably not a good idea to leak in all environment variables from the login node and it ought to not need any of them.

#SBATCH --exclusive

export NCCL_SOCKET_IFNAME=eth0
export SHARP_COLL_ENABLE_PCI_RELAXED_ORDERING=1
export NCCL_COLLNET_ENABLE=0
export NCCL_IB_HCA=ibp
export UCX_NET_DEVICES=ibp0:1,ibp1:1,ibp2:1,ibp3:1,ibp4:1,ibp5:1,ibp6:1,ibp7:1
Comment on lines +12 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These environment variables assume very specific things and as such aren't appropriate if this is meant to be trivially ran by users. It assumes:

  • An ethernet NIC for TCP/IP comms (this is probably a totally fine assumption)
  • Tries to run SHARP if possible (as long as this doesn't error out if a node doesn't have SHARP this is also fine)
  • Disables the NCCL CollNet plugin. This shouldn't be unconditionally disabled, and for what it's worth disabling it may not allow SHARP in the first place.
  • Assumes the node running has IB and exactly 8 ports

If this is meant to be pretty well generalizable as a demo script we may as well just remove these environment variables entirely and let NCCL figure these out on its own.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very fair, and also echoes Brad's suggestion that we keep GH200 in mind as well. Considering an environment where these settings would actually be appropriate/optimal, what would be the impact of removing them? Just a little extra setup time as NCCL probes the topology?

A simple path forward could be to just do a couple of different versions of this demo with settings pre-optimized for different topologies. A higher LOE path for the future could be a script that figures out what the environment is and chooses recommended env vars based on what it finds.


export MASTER_PORT="$(expr 10000 + "$(echo -n "${SLURM_JOB_ID:?}" | tail -c 4)")"
export MASTER_ADDR="$(scontrol show hostnames "${SLURM_JOB_NODELIST:?}" | head -n 1)"

# This will be used for both the output directory as well as the wandb project
export PROJECT_NAME="test"

# If you change this, make sure the --container-mount can find it
export OUTDIR="/mnt/data/coreweave/${PROJECT_NAME}"

# Megatron's dataset indices will go here once built
export CACHEDIR="${OUTDIR}/cache"

# save images here
export CONTAINER_DIR="/mnt/data/coreweave/images"

# uncomment if running on H100/H200
#CPU_BIND='map_ldom:0,0,0,0,1,1,1,1'


# Use squished container if available, otw download and save it.
export REMOTE_IMAGE_URI="ghcr.io#coreweave/ml-containers/megatron:dmarx-megatron-update-0c68584"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last I checked valid GitHub credentials need to be figured here via enroot to download this. Is this documented?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this container is public, does that still require gh creds?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any event, I'll add that to the instructions. got it written up for the megatron demo already anyway.

export IMAGE_NAME="training-test.sqsh"
export LOCAL_IMAGE_PATH="${CONTAINER_DIR}/${IMAGE_NAME}"

CONTAINER_SAVE=""
if [ -f "$LOCAL_IMAGE_PATH" ]; then
CONTAINER_IMAGE="${LOCAL_IMAGE_PATH}"
else
CONTAINER_IMAGE="${REMOTE_IMAGE_URI}"
CONTAINER_SAVE="--container-save=${LOCAL_IMAGE_PATH}"
fi


srun --container-image "${CONTAINER_IMAGE}" ${CONTAINER_SAVE:+"$CONTAINER_SAVE"} \
--container-mounts /mnt/data:/mnt/data \
--export=ALL \
--mpi=pmix \
--kill-on-bad-exit=1 \
${CPU_BIND:+"--cpu-bind=$CPU_BIND"} \
bash <<'EOF'
#### in-line srun script ####
#!/bin/bash

export CUDA_DEVICE_MAX_CONNECTIONS=1

export WORLD_SIZE="${SLURM_NTASKS:?}"
export RANK="${SLURM_PROCID:?}"
export LOCAL_RANK="${SLURM_LOCALID:?}"
export CUDA_DEVICE_ORDER='PCI_BUS_ID'
Comment on lines +61 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are unnecessary when using torchrun fwiw. If our container has this I strongly recommend we make this the preferred pattern. torchrun is automatically installed when installing torch, so we should be able to assume it's available.



CKPTDIR_LOAD="${CKPTDIR_LOAD:-${OUTDIR}/checkpoints}"
CKPTDIR_SAVE="${CKPTDIR_SAVE:-${OUTDIR}/checkpoints}"
mkdir -p "${CKPTDIR_SAVE}"


touch "${CKPTDIR_SAVE}/progress.txt"


cd /usr/src/app/megatron-lm

WARNING_FILTERS=(
'-Wignore::DeprecationWarning'
'-Wignore::FutureWarning'
'-Wignore::UserWarning:megatron.core.tensor_parallel.layers' # "async_grad_allreduce is deprecated"
'-Wignore::UserWarning:megatron.core.optimizer.distrib_optimizer' # "pre_hook" method deprecations
)

WANDB_ARGS=()
# Enable W&B flags only if credentials are available via ~/.netrc or env var
if [ -f "${HOME}/.netrc" ] || [ -n "${WANDB_API_KEY:-}" ] || [ -n "${WANDB_APIP_KEY:-}" ]; then
WANDB_ARGS+=("--wandb-exp-name" "${SLURM_JOB_ID:?}/megatron-test")
WANDB_ARGS+=("--wandb-project" "${PROJECT_NAME}")
WANDB_ARGS+=("--wandb-save-dir" "${OUTDIR}/logs")
fi

python3 "${WARNING_FILTERS[@]:?}" \
"/usr/src/app/megatron-lm/pretrain_gpt.py" \
${WANDB_ARGS[@]+"${WANDB_ARGS[@]}"} \
--tensorboard-dir "${OUTDIR}/tensorboard" \
--data-cache-path ${CACHEDIR} \
--load "${CKPTDIR_LOAD}" \
--save "${CKPTDIR_SAVE}" \
--save-interval 3500 \
--tensor-model-parallel-size 4 \
--pipeline-model-parallel-size 1 \
Comment on lines +102 to +103

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assumes the job's available VRAM can support 2 separate model replicas during training. Since we can't know the exact VRAM a generic job will have, setting the tensor parallelism to the world size is probably a good defensive option.

--context-parallel-size 1 \
--sequence-parallel \
--overlap-grad-reduce \
--overlap-param-gather \
--train-iters 2507400 \
--lr 4e-06 \
--min-lr 4e-07 \
--lr-warmup-iters 2000 \
--lr-decay-iters 2507400 \
--lr-decay-style cosine \
--clip-grad 1.0 \
--bf16 \
--use-flash-attn \
--rotary-seq-len-interpolation-factor 32 \
--no-fp8-wgrad \
--use-distributed-optimizer \
--distributed-backend nccl \
--split 949,50,1 \
--seed 42 \
--use-checkpoint-args \
--no-masked-softmax-fusion \
--attention-softmax-in-fp32 \
--transformer-impl transformer_engine \
--attention-dropout 0.0 \
--hidden-dropout 0.0 \
--rotary-base 500000 \
--rotary-percent 1.0 \
--use-rope-scaling \
--micro-batch-size 1 \
--log-interval 1 \
--tensorboard-log-interval 1 \
--eval-interval 100 \
--eval-iters 10 \
--logging-level 20 \
--log-params-norm \
--log-num-zeros-in-grad \
--log-throughput \
--log-progress \
--timing-log-level 0 \
--timing-log-option all \
--log-timers-to-tensorboard \
--log-validation-ppl-to-tensorboard \
--log-memory-to-tensorboard \
--log-world-size-to-tensorboard \
--ffn-hidden-size 11008 \
--num-attention-heads 32 \
--num-layers 32 \
--hidden-size 4096 \
--seq-length 8192 \
--max-position-embeddings 8192 \
Comment on lines +150 to +153

@sangstar sangstar Mar 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again makes assumptions about VRAM capacity for jobs, but you need something for these so it's kind of unavoidable that there won't be a magic set of parameters that will always work. Could maybe considering defensively reducing these by a factor of 2 but it's a nitpick.

--untie-embeddings-and-output-weights \
--normalization RMSNorm \
--swiglu \
--position-embedding-type rope \
--disable-bias-linear \
--group-query-attention \
--num-query-groups 8 \
--mock-data
EOF