#!/usr/bin/env bash # Submit a multi-node Slurm training job. # # Usage: # bash examples/train/run_slurm.sh [--dotted.key value ...] # # Examples: # bash examples/train/run_slurm.sh examples/train/configs/example.yaml 2 # bash examples/train/run_slurm.sh examples/train/configs/distribution_matching/wan/dmd2_t2v.yaml 4 \ # --training.optimizer.learning_rate 1e-5 # bash examples/train/run_slurm.sh examples/train/configs/example.yaml 8 \ # --training.checkpoint.resume_from_checkpoint outputs/my_run/checkpoint-1000 # # Environment variables (override defaults): # PARTITION Slurm partition (default: main) # NUM_GPUS GPUs per node (default: 8) # CPUS_PER_TASK CPUs per task (default: 128) # MEM Memory per node (default: 1440G) # JOB_NAME Slurm job name (default: derived from config) # OUTPUT_DIR Directory for slurm logs (default: slurm_logs) # MASTER_PORT Rendezvous port (default: 29500) # EXCLUDE Nodes to exclude (default: "") # WANDB_API_KEY Exported W&B API key required for online logging # WANDB_MODE W&B mode (default: online) # SBATCH_CONSTRAINT Slurm node constraint (default: "") # SBATCH_TIMELIMIT Slurm time limit (default: "") set -euo pipefail CONFIG="${1:?Usage: $0 [extra flags...]}" NUM_NODES="${2:?Usage: $0 [extra flags...]}" shift 2 # ── Defaults ────────────────────────────────────────────────────── PARTITION="${PARTITION:-main}" NUM_GPUS="${NUM_GPUS:-8}" CPUS_PER_TASK="${CPUS_PER_TASK:-128}" MEM="${MEM:-1440G}" MASTER_PORT="${MASTER_PORT:-29500}" EXCLUDE="${EXCLUDE:-}" WANDB_MODE="${WANDB_MODE:-online}" SBATCH_CONSTRAINT="${SBATCH_CONSTRAINT:-}" SBATCH_TIMELIMIT="${SBATCH_TIMELIMIT:-}" TOTAL_GPUS=$(( NUM_NODES * NUM_GPUS )) CONFIG_NAME="$(basename "${CONFIG}" .yaml)" JOB_NAME="${JOB_NAME:-${CONFIG_NAME}}" OUTPUT_DIR="${OUTPUT_DIR:-logs/slurm}" mkdir -p "${OUTPUT_DIR}" # ── Build sbatch args ───────────────────────────────────────────── # Forward the exported W&B API key through Slurm's environment so the generated # job script and its xtrace output do not contain the credential value. SBATCH_ARGS=( --job-name="${JOB_NAME}" --partition="${PARTITION}" --nodes="${NUM_NODES}" --ntasks="${NUM_NODES}" --ntasks-per-node=1 --gres="gpu:${NUM_GPUS}" --cpus-per-task="${CPUS_PER_TASK}" --mem="${MEM}" --output="${OUTPUT_DIR}/${JOB_NAME}_%j.out" --error="${OUTPUT_DIR}/${JOB_NAME}_%j.err" --export="ALL,WANDB_MODE=${WANDB_MODE}" --exclusive ) if [[ -n "${EXCLUDE}" ]]; then SBATCH_ARGS+=(--exclude="${EXCLUDE}") fi # Apply hardware and wall-clock requirements selected by an experiment launcher. if [[ -n "${SBATCH_CONSTRAINT}" ]]; then SBATCH_ARGS+=(--constraint="${SBATCH_CONSTRAINT}") fi if [[ -n "${SBATCH_TIMELIMIT}" ]]; then SBATCH_ARGS+=(--time="${SBATCH_TIMELIMIT}") fi # ── Collect extra overrides for the training script ─────────────── EXTRA_ARGS=("$@") # Shell-escape values before embedding them in the generated Bash script so # config paths and override arguments retain their original boundaries. printf -v CONFIG_SHELL_ARG '%q' "${CONFIG}" EXTRA_ARGS_SHELL="" if (( ${#EXTRA_ARGS[@]} > 0 )); then printf -v EXTRA_ARGS_SHELL ' %q' "${EXTRA_ARGS[@]}" fi echo "=== Slurm Training Submission ===" echo "Config: ${CONFIG}" echo "Nodes: ${NUM_NODES}" echo "GPUs/node: ${NUM_GPUS}" echo "Total GPUs: ${TOTAL_GPUS}" echo "Partition: ${PARTITION}" echo "Job name: ${JOB_NAME}" echo "Extra args: ${EXTRA_ARGS[*]:-}" echo "=================================" # ── Submit ──────────────────────────────────────────────────────── sbatch "${SBATCH_ARGS[@]}" <