Array Jobs

Job arrays are used for running the same job a large number of times very efficiently for Slurm with only slight differences between the jobs. For instance, let's say that you need to run 10 jobs, each with a different seed value for the random number generator. Job arrays are the best choice for such cases.

If you ever feel the need to do something like this:

 for i in {1..10} ; do
     sbatch runscript.sh ${i}.in
 done

Don't do it! Use Job Arrays instead!

Below is an example Slurm script for running Python where there are 10 jobs in the array:

#!/bin/bash
#SBATCH --job-name=array-job     # create a short name for your job
#SBATCH --output=slurm-%A_%a.out # stdout file, %A -> SLURM_ARRAY_JOB_ID, %a -> SLURM_ARRAY_TASK_ID
#SBATCH --error=slurm-%A_%a.err  # stderr file
#SBATCH --nodes=1                # node count
#SBATCH --ntasks=1               # total number of tasks across all nodes
#SBATCH --cpus-per-task=1        # cpu-cores per task (>1 if multi-threaded tasks)
#SBATCH --mem-per-cpu=4G         # memory per cpu-core (4G is default)
#SBATCH --time=00:01:00          # total run time limit (HH:MM:SS)
#SBATCH --array=0-9              # job array with index values 0,1,2,...,9
#SBATCH --mail-type=all          # send email on job start, end and fault
#SBATCH --mail-user=noreply@uni-a.de

echo "My SLURM_ARRAY_JOB_ID is $SLURM_ARRAY_JOB_ID."
echo "My SLURM_ARRAY_TASK_ID is $SLURM_ARRAY_TASK_ID"
echo "Executing on the machine:" $(hostname)

# Loading modules here

srun python myscript.py $SLURM_ARRAY_TASK_ID

The key line in the Slurm script above is:

#SBATCH --array=0-9

Job Array limits

  • The array numbers i (values of SLURM_ARRAY_TASK_ID ) must satisfy the equation 0 ≤ i < 1001. If you need numbers outside of this range, see below.
  • You cannot have more than 1000 members of an Array Job.
  • filename patterns (%A -> SLURM_ARRAY_JOB_ID, %a -> SLURM_ARRAY_TASK_ID) in --output and --error  options are only valid for Array Jobs.


In this example, the Slurm script will run three jobs. Each job will have a different value of SLURM_ARRAY_TASK_ID (i.e., 0, 1, 2, 3, 4, 5, 6, 7, 8, 9). The value of SLURM_ARRAY_TASK_ID can be used to differentiate the jobs within the array. The following environment variables will be available:

SLURM_ARRAY_TASK_ID=0
SLURM_JOB_ID=354
SLURM_ARRAY_JOB_ID=353
SLURM_ARRAY_TASK_ID=0
SLURM_ARRAY_TASK_COUNT=10
SLURM_ARRAY_TASK_MAX=9
SLURM_ARRAY_TASK_MIN=0
SLURM_ARRAY_TASK_STEP=1
SLURM_ARRAY_TASK_ID=1
SLURM_JOB_ID=355
SLURM_ARRAY_JOB_ID=353
SLURM_ARRAY_TASK_ID=1
SLURM_ARRAY_TASK_COUNT=10
SLURM_ARRAY_TASK_MAX=9
SLURM_ARRAY_TASK_MIN=0
SLURM_ARRAY_TASK_STEP=1


...
...
...
...
...
...
...
...


SLURM_ARRAY_TASK_ID=9
SLURM_JOB_ID=353
SLURM_ARRAY_JOB_ID=353
SLURM_ARRAY_TASK_ID=9
SLURM_ARRAY_TASK_COUNT=10
SLURM_ARRAY_TASK_MAX=9
SLURM_ARRAY_TASK_MIN=0
SLURM_ARRAY_TASK_STEP=1


Note the SLURM_ARRAY_JOB_ID is the same for all sub-tasks in the Job Array, while the usual SLURM_JOB_ID variable will be different. Also for the last sub-task in a Job Array SLURM_JOB_ID will be equal to SLURM_ARRAY_JOB_ID .


By default, as many sub-tasks of an Array Job as possible will be started immediately. On an empty cluster, all sub-tasks will be started at once given there are enough free resources.

Please, be respectful to your colleagues and limit the number n (not the total number of tasks) of concurrent sub-tasks in an Array Job to a reasonable value. You just need to append "%n" to the array option, see for example n=4 case:

#SBATCH --array=0-9%4


You can set the array numbers to different sets of numbers or one or multiple ranges, for example:

#SBATCH --array=0,100,200,300,400,500
#SBATCH --array=1-24,42,56-99
#SBATCH --array=0-1000

Be sure to use the value of SLURM_ARRAY_TASK_ID to assign unique names to the output files for each job in the array when using the --output and --error options or redirecting stdout and/or stderr to a file manually. Failure to do this will result in undefined behaviour when all jobs are using the same filename(s). 

If you are unsure, you can skip the --output and --error options entirely and by default there will be a single (combined stdout+stderr) file called:

slurm-${SLURM_ARRAY_JOB_ID}_${SLURM_ARRAY_TASK_ID}.out

Each job in the array will have the same values for nodes, ntasks, cpus-per-task, time and so on. This means that job arrays can be used to handle everything from serial jobs to large multi-node cases.

See the SLURM documentation for more information.



Chain Jobs

If all your sub-tasks depend on one another, you can also limit the concurrency to n=1. It is common to talk about a Chain Job in this case, because a sub-task will only start if the previous one has ended.

#SBATCH --array=0-9%1
Schematic example to check if programm is completed
 #!/bin/bash
#SBATCH --job-name=array-job
#SBATCH --output=slurm-%A.%a.out
#SBATCH --error=slurm-%A.%a.err
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=1
#SBATCH --mem-per-cpu=4G
#SBATCH --time=00:01:00
#SBATCH --array=0-9%1
#SBATCH --mail-type=all
#SBATCH --mail-user=noreply@uni-a.de

srun ./myprogram

If your programm periodically writes its state to a file on disk and if your calculation may be resumed based on this state, then Chain Jobs may be used to overcome the partition time limit. Whenever one sub-task is killed by SLURM when running into the TIMEOUT event, the next sub-task will continue where it left off.

Beware

If you use this type of jobs you have to make sure to to put in a check if the progamm is already completed, and if yes, turn all remaining array tasks into a no-op that complete immediately. This is necessary because the number of tasks in a Job Array cannot be updated after submission. Do not set the number of repetitions ( 9 in this example) too high.

Schematic example to check if programm is completed
 #!/bin/bash
#SBATCH --job-name=array-job
#SBATCH --output=slurm-%A.%a.out
#SBATCH --error=slurm-%A.%a.err
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=1
#SBATCH --mem-per-cpu=4G
#SBATCH --time=00:01:00
#SBATCH --array=0-9%1
#SBATCH --mail-type=all
#SBATCH --mail-user=noreply@uni-a.de

if [[ <Insert check if your calculation is completed > ]] ; then
	exit 0
fi

srun ./myprogram

Advanced Task IDs

You may also introduce an increment to array ranges, for example:

#SBATCH --array=0-7:2
# is the same as
#SBATCH --array=0,2,4,6
# may be even combined with multiple ranges
#SBATCH --array=0-2:2,4-9:2,12
# may be even combined with concurrent job limits
#SBATCH --array=0-2:2,4-9:2,12%4

 If you need numbers i outside the range 0 ≤ i < 1001, use bash arithmetics to generate the necessary numbers based on SLURM_ARRAY_TASK_ID .

MYVAR=$((SLURM_ARRAY_TASK_ID * 1000 + 25))
let MYVAR=SLURM_ARRAY_TASK_ID*1000+25 # modern bash
# if you need leading zeros:
MYVAR=$(printf "%05d" $myvar);