Running Jobs with Slurm¶
Slurm (Simple Linux Utility for Resource Management) is the workload manager. You describe what resources your job needs (CPUs, memory, time, GPUs), and Slurm schedules your job to run when those resources are available. You never SSH directly to a compute node — Slurm handles that for you.
Key Slurm Concepts¶
- Partition: A logical group of nodes, often differentiated by hardware
(e.g.,
general,astro,teraram,teslap100). Choose the right one for your job. - Job: A unit of work submitted to Slurm. Can be a single task or a complex multi-node parallel job.
- Allocation: The set of resources Slurm reserves for your job.
Useful Slurm Commands¶
# View the job queue
squeue
# View only your jobs
squeue -u your_username
# View available partitions and their status
sinfo
# Cancel a job
scancel JOB_ID
# Detailed accounting for a completed job
sacct -j JOB_ID --format=JobID,JobName,Elapsed,CPUTime,MaxRSS,State
The Partition System¶
A partition is a named group of nodes with its own access rules and limits.
Select one with #SBATCH --partition=<name> (or -p <name>). Most users only
ever need general.
| Partition | Purpose | Who can use it | Nodes | Max walltime |
|---|---|---|---|---|
general |
Default, general-purpose CPU compute | generalusers group (all standard accounts) |
36 nodes (3,872 CPUs), Broadwell → Sapphire Rapids + AMD | 30 days |
astro |
Astrophysics group | astro group |
meitner (144 CPUs) |
30 days |
teraram |
Big-memory jobs | teo group |
feynman (96 CPUs, 1.25 TB RAM) |
30 days |
teslap100 |
GPU jobs | teo group |
cabibbo (NVIDIA Tesla P100, 32 CPUs) |
30 days |
Access is controlled by Unix group membership — if you are not in the
group, sbatch -p <that partition> is rejected. Standard accounts are in
generalusers and so can use general. Check your groups with id; see live
partition state and exact node counts with sinfo.
Choosing a partition
Start with general. Reach for a specialised partition only when your job
genuinely needs it — a terabyte of RAM (teraram) or a GPU (teslap100) — and
you have access. general holds the overwhelming majority of the capacity.
How Slurm Counts CPUs Here¶
The farm schedules with CR_Core and SMT (hyper-threading) enabled, which has
two consequences worth internalising:
- A Slurm "CPU" is a hardware thread, and jobs are allocated whole physical
cores. So
--cpus-per-task=8reserves 8 threads = 4 physical cores (both hyper-threads of each). The CPU counts shown bysinfo(%C) are threads. - Always request memory explicitly. If you omit
--mem, you do not get "as much as the node has" — you get the cluster default ofDefMemPerCPU= 1000 MB per allocated CPU. A single-CPU job therefore gets 1 GB, and is killed the moment it exceeds that. This is a common and confusing first failure: the code works fine interactively but the batch job dies with an out-of-memory kill. Use--mem=<total>or--mem-per-cpu=<per thread>.
--mem.
Tools that generate their own Slurm submissions (MadGraph's
cluster.py, for example) may not forward a memory request at
all, leaving their jobs on the 1000 MB-per-CPU default and dying at scale
for no visible reason. If you are driving Slurm through a framework rather
than writing #SBATCH lines yourself, check what it actually
submits before blaming your physics.
Free CPUs are not enough — you also need free memory
A node can show many idle CPUs yet have little free RAM (most of it committed to running jobs). Your job starts only when a slot has both enough free CPUs and enough free memory. Check both at once:
Smaller, shorter jobs also backfill into schedule gaps sooner than large, long ones — right-size your request.Batch Jobs¶
A batch job is the standard workflow: you write a shell script describing your job and its resource requirements, submit it, and retrieve results when it finishes. This is ideal for long runs, parameter sweeps, and any job that does not need interactive supervision.
Anatomy of a Slurm Batch Script¶
Lines beginning with #SBATCH are directives to the Slurm scheduler — they
are not executed as shell commands, but parsed before your job starts.
#!/bin/bash
# ─────────────────────────────────────────────────────
# Slurm directives
# ─────────────────────────────────────────────────────
#SBATCH --job-name=thrust_nnlo # A human-readable name for your job
#SBATCH --partition=general # Which partition to run on
#SBATCH --nodes=1 # Number of compute nodes
#SBATCH --ntasks=1 # Number of parallel tasks (MPI ranks)
#SBATCH --cpus-per-task=8 # CPU cores per task (for OpenMP/threads)
#SBATCH --mem=16G # Total memory for the job
#SBATCH --time=04:00:00 # Maximum wall time (days-HH:MM:SS)
#SBATCH --output=logs/job_%j.out # Standard output (%j = job ID)
#SBATCH --error=logs/job_%j.err # Standard error
#SBATCH --mail-type=END,FAIL # Email on completion or failure
#SBATCH --mail-user=user@lcm.mi.infn.it
# ─────────────────────────────────────────────────────
# Environment setup
# ─────────────────────────────────────────────────────
# REQUIRED: batch scripts are non-login shells, where `module`
# does not exist until this file is sourced. Without it, every
# `module load` below fails silently and you get system GCC 11.5.
source /etc/profile.d/modules.sh
module purge
module load gcc/13
module load mpi/mpich-x86_64
# Cheap insurance — confirm you got the compiler you asked for
echo "Compiler: $(gcc --version | head -1)"
# ─────────────────────────────────────────────────────
# Job execution
# ─────────────────────────────────────────────────────
# Set OpenMP thread count to match requested CPUs
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
echo "Job started on $(hostname) at $(date)"
echo "Running with $SLURM_CPUS_PER_TASK threads"
# Change to temporary for faster I/O
cd $SLURM_TMPDIR
# Run your compiled executable
srun thrust_calculator --order NLO --output results_nnlo.dat
echo "Job finished at $(date)"
Submit the script with:
A Fortran/C++ Compilation + Run Example¶
Because you cannot compile on the login node, a common pattern is to use a short "build job" first, then a "run job":
#!/bin/bash
#SBATCH --job-name=compile_mc
#SBATCH --partition=general
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --mem=4G
#SBATCH --time=00:30:00
#SBATCH --output=logs/compile_%j.out
#SBATCH --mail-type=END,FAIL # Email on completion or failure
#SBATCH --mail-user=user@lcm.mi.infn.it
source /etc/profile.d/modules.sh
module purge
module load gcc/15
echo "Compiler: $(gcc --version | head -1)" # expect 15.2.1
cd /home/your_username/myproject
mkdir -p build && cd build
# Use -march=x86-64-v3 (not -march=native) so the binary runs on every node.
# See System Architecture → Instruction Sets for why.
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -march=x86-64-v3"
make -j $SLURM_CPUS_PER_TASK
echo "Build complete."
Once the build job finishes (check with squeue or wait for the email), your
executable is ready and you can submit your production run job.
Job Arrays — Running Parameter Sweeps¶
If you need to run the same code with many different inputs (e.g., different values of a coupling constant, different scales μ), use a job array instead of submitting dozens of identical scripts:
#!/bin/bash
#SBATCH --job-name=scale_scan
#SBATCH --array=1-50 # Launches 50 jobs, SLURM_ARRAY_TASK_ID = 1..50
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --mem=8G
#SBATCH --time=02:00:00
#SBATCH --output=logs/scan_%A_%a.out # %A = array job ID, %a = task ID
source /etc/profile.d/modules.sh
module purge
module load gcc/14
# Use the task ID to select a parameter from a list
SCALE_FILE=/home/your_username/scales.txt
SCALE=$(sed -n "${SLURM_ARRAY_TASK_ID}p" $SCALE_FILE)
echo "Running with scale mu = ${SCALE} GeV"
./my_program --mu $SCALE --output results/output_${SLURM_ARRAY_TASK_ID}.dat
A job array of 500 tasks could flood the queue. Use the
%N suffix to cap concurrent running tasks:
#SBATCH --array=1-500%20 runs at most 20 simultaneously.
Interactive Sessions¶
Sometimes you need to work interactively on a compute node — for example, to
test a compilation, debug a script, or run Mathematica interactively. Use
salloc or srun for this.
An interactive session holds a reservation on compute nodes for its entire
duration, even if you are idle. Request only what you need, and
exit your session as soon as you are done.
salloc — Request an Allocation, Then Work¶
salloc requests resources and drops you into a new shell. From there, you
can run commands directly on the allocated node(s).
# Request 1 node, 4 CPUs, 8 GB RAM, for up to 2 hours.
# Use `--pty bash -l` — the -l (login shell) is what makes `module` available.
salloc --nodes=1 --ntasks=1 --cpus-per-task=4 --mem=8G --time=02:00:00 \
--pty bash -l
# Once the allocation is granted, your prompt changes.
# You are now running on a compute node.
# Load modules and work interactively:
module load gcc/15
make -j 4
./my_program --test
# When done, exit the allocation
exit
-l, module will not exist.
A bare salloc ... --pty bash gives you a non-login shell, in
which module load fails with command not found — the
same trap as in batch scripts. Either use --pty bash -l as
above, or run source /etc/profile.d/modules.sh once you are in.
srun — Run Commands on Compute Nodes¶
srun is used to launch tasks directly on compute nodes under Slurm allocation. It’s ideal for:
- Running a single command non-interactively
- Launching parallel jobs
- Opening an interactive session on a compute node
Unlike sbatch, srun runs jobs immediately (subject to scheduling), and unlike ssh, it ensures proper resource accounting and isolation.
Run a Single Command¶
Use srun when you need to execute one command with allocated resources:
# Compile using 8 CPU cores on a compute node
srun --ntasks=1 --cpus-per-task=8 --mem=4G --time=00:15:00 \
make -j8
Interactive Shell on a Compute Node¶
To debug, test code, or run commands manually:
# Open an interactive bash shell on a compute node.
# `-l` makes it a login shell so that `module` is available.
srun --ntasks=1 --cpus-per-task=4 --mem=8G --time=01:00:00 --pty bash -l
This gives you a shell inside a compute node, not the login node.