
Multithreaded Job Submission Example
This page shows an example of submitting a job that uses multithreading for parallelization. This example is for purely multithreaded jobs without any MPI parallelization --- for an example of a job in which both MPI and multithreaded are used, see the hybrid job example.
Many of the codes used on HPC clusters support parallelizatin via MPI, and so this example can generally be adapted to support such.
Overview
This example basically consists of a single file, the job script
submit.sh (see for a listing and explanation
of the script) which gets submitted to the cluster via the
sbatch command.
The script is designed to show many good practices; including: - setting standard sbatch options within the script - loading the needed modules within the script - printing some useful diagnostic information at start of the script - creating a job specific temporary work directory - running the code and saving the exit code - copying back any files that should be retained - exiting with the exit code from the main application
Many of the practices above are rather overkill for such a simple job --- indeed, the vast majority of lines are for these "good practices" rather than the running of the intended code, but are included for educational purposes.
The example shown is a rather trivial Hello World type program in which each thread prints out a "Hello" message identifying the thread number and task number (always task 0 of 1 for this case), along with the hostname and process ID. Although trivial, it shows the basics of submitting such a job, along with various other good practices, including:
- setting standard sbatch options within the script
- loading the needed modules within the script
- printing some useful diagnostic information at start of the script
- creating a job specific temporary work directory
- running the code and saving the exit code
- copying important output files back to the submission directory
- exiting with the exit code from the main application
Many of the practices above are rather overkill for such a simple job --- indeed, the vast majority of lines are for these "good practices" rather than the running of the intended code, but are included for educational purposes.
The submission script
The submission script can be downloaded as plain text, suitable for you to submit to the scheduler with the sbatch command. We present a copy with line numbers for discussion below.
Source of submit.sh
The line numbers in the listing below are to facilitate a line-by-line discussion pf the script; they should not present in the actual script. Downloaded as plain text version of the script.
Example
1 #!/bin/bash
2 # The line above this is the "shebang" line. It must be first line in script
3 #-----------------------------------------------------
4 # Example Submission Script: Hello-UMD, multithreaded
5 # Runs a multithreaded hello-umd command
6 #-----------------------------------------------------
7 #
8 # Slurm sbatch parameters section:
9 # Request a single task using 10 CPU cores
10 #SBATCH --ntasks=1
11 #SBATCH --cpus-per-task=10
12 # Request 5 minute of walltime
13 #SBATCH -t 5
14 # Request 10 GiB of total memory for the job
15 #SBATCH --mem=10240
16 # Allow other jobs to run on same node
17 #SBATCH --oversubscribe
18 # Run on debug partition for rapid turnaround. You will need
19 # to change this (remove the line) if walltime > 15 minutes
20 #SBATCH --partition=debug
21 # This job will run our hello-umd demo binary in multithreaded mode
22 # Output will go to local /tmp scratch space on the node we are running
23 # on, and then will be copied back to our work directory.
24 # Process our dot files (and in particular define the "module" command)
25 source /etc/profile
26 # Module load section
27 # First clear our module list
28 module purge
29 # and reload the standard modules
30 module load hpcc/zaratan
31 # Set up environment for hello-umd
32 module load hello-umd
33 # Section to make a scratch directory for this job
34 # Since this is a single node job, the local /tmp filesystem is a good choice
35 # We include the SLURM jobid in the #directory name to avoid interference if
36 # multiple jobs running at same time.
37 TMPWORKDIR="/tmp/job.${SLURM_JOB_ID}"
38 mkdir $TMPWORKDIR
39 cd $TMPWORKDIR
40 # Section to output information identifying the job, etc.
41 echo "Slurm job submit-hello-umd-multithreaded.sh ${SLURM_JOB_ID} running on:"
42 hostname
43 echo "Ntasks=${SLURM_NTASKS}; CPU cores per task=${SLURM_CPUS_PER_TASK}"
44 echo "On ${SLURM_JOB_NUM_NODES} nodes: ${SLURM_JOB_NODELIST}"
45 date
46 pwd
47 echo "Loaded modules are:"
48 module list
49 # Run our code, giving -t 0 to use all available CPUs (10 in this case)
50 hello-umd -t 0 > hello.out 2>&1
51 # Save the exit code from the previous command
52 ECODE=$?
53 # Copy results back to submit dir
54 cp hello.out ${SLURM_SUBMIT_DIR}/hello-${SLURM_JOB_ID}.out
55 echo "Job finished with exit code $ECODE"
56 date
57 # Exit with the cached exit code
58 exit $ECODE
Discussion of submit.sh
The following is a detailed, more or less line-by-line discussion of the job submission script:
- Line 1: This shebang line
specifies that the script is to be processed using the
bash shell.
For more information on the shebang line. - Lines 2-7: These are comment lines,
which are ignored by the bash shell.
They are only present to make the script more readable to humans. For more information on comment lines. - Lines 8-20: These lines specify various flags to be passed to the sbatch command. For more information about #SBATCH directives.
- Lines 10-11: As this is a multithreaded job, we request a single single task. with 10 CPU cores per task, for a total of 10 cores. As all resources for a task are allocated to the same node by the scheduler, these 10 cores will all be allocated on the same node.
- Line 13: This is a trivial job, so we only request 5 minutes for it. It should finish in well under 5 minutes. For more information about maximum walltime settings
- Line 15: This is a trivial job which does not need much
RAM, so we request a total of 10 GiB
for it, and even that is well over what the job probably needs. We
used the
--memflag to request the total memory for the job; we could have used--mem-per-cpu=1024to request 1 GiB per CPU core (which also would allocation 10 GiB for the job), but for multithreaded jobs the memory requirement is typically independent of the number of threads, so specifying the total amount of memory is usually more convenient. For more information about memory settings - Line 17: This
#SBATCHdirective says that we are allowing other jobs to share the same node(s) as our job. This is the default (so this directive can usually be omitted) and is recommended, especially for jobs using only a fraction of the cores on a node.
Otherwise, a significant number of cores would not be used by your job and would sit idly since other jobs would be prevented from using them --- this is an inefficient use of the cluster and your allocation account. For more information about exclusive/shared mode. - Line 20: This tells the scheduler to put our job in the debug partition. Since this is a short tutorial job, the debug partition is fine and generally will allow the job to spend less time in the queue. For more information about selecting partitions.
- Lines 24-32: These lines set up the
environment within which to run your job. These are explained in
more detail in the main job examples
page, but in this case the lines
basically:
- Ensure the module command is defined
- Clear out any previously loaded modules (inherited from .bashrc or similar)
- Load the module for hello-umd
- Lines 33-39: These lines set up a work directory for our job, in this case since it is a multithreaded job and therefore necessarily a single node job, we use a work directory in the node local /tmp filesystem. For more information about setting up work directories.
- Lines 40-48: These lines print out various pieces of information to identify the job. The output will go into the file "slurm-JOB_NUMBER.out" in the directory from which you issued the sbatch command (where JOB_NUMBER is the Slurm job number). There are options you can pass to sbatch to change the name of the file.
- Line 50: This line actually runs the
hello-umdcommand, saving all output to the filehello.outin the temporary working directory created in lines 33-39. I.e., this one line is the meat of the job script; everything else basically just exists to get to this point or cleanup afterwards.
When writing your own job script to do something useful, this is where you will be putting most of your effort, possibly expanding to multiple lines.
In this simple case, we basically just run the hello-umd command with the flag-t 0. The -t flag instructs hello-umd to run with the specified number of threads; by default it runs a single thread.
The value 0 is a special value meaning that it should start a thread on every CPU core it finds; the scheduler is set up so that the job can only see the cores allocated to it, so this will run one thread for each of the 10 cores allocated to the job. We could also have used-t $SLURM_CPUS_PER_TASKwhich would have doen the same thing (using the SLURM_CPUS_PER_TASK variable set by Slurm to the value of the--cpus-per-taskor-coption from line 11). You could have also simple used-t 10, but we recommend using one of the other methods for consistency; at some point you might try running with a different number of threads, and if you used-t 0or-t $SLURM_CPUS_PER_TASKin this line you would only need to change the value in line 11. Otherwise you would have to change both lines, and if they are not in sync it would lead to in efficient usage of the cluster.
The "> hello.out" part of this command means that the standard output from the command will be saved in the filehello.outin the working directory. The "2>&1" part means that any output to the standard error output stream will be redirected to the standard output. (NOTE: if you wish to save both standard output and standard error to the same file, you need to give these output redirection arguments in that order; redirect standard output first, then redirect standard error to standard output.) - Line 52: This line saves the exit code
from the
hello-umdcommand so thatt be used when the script ends.
For more information about exit codes. - Lines 53-56: These lines just do some cleanup and other minor tasks at the end of the job.
- Line 54: This line copies the
hello.outfile from our working directory in the node local /tmp filesystem back to the directory from which we submitted the job (i.e. where you ran the sbatch command). We add the Slurm job number to the file name during the copy; this prevents the clobbering of the output file if we submit the job multiple times.
This step is needed because we were using the node local /tmp filesystem, which will be deleted after the job terminates. If you modified the script to use the scratch filesystem instead, you can skip this set, as the scratch filesystem is more permanent.
NOTE: When using the node local /tmp filesystem, you generally need to do this or a similar transfer of any output files, as Slurm will delete any data in the /tmp filesystem after your job terminates. - Lines 55-56: These lines print
some additional information just before the job terminates.
These lines will go in the same file as those from the start of the script, usually "slurm-JOBNUMBER.out".
This just prints out:- The exit code from the
hello-umdcommand (as stashed in theECODEenvironment variable) - The time when the job finished
- The exit code from the
- Line 58: This line actually ends the job
script, and terminates the job. The script and job will actually
end at the end of the file even without an explicit
exitcommand, but in that case the exit code for the job script will be that of the last command which ran, which typically will be part of the cleanup commands, and not the meat of the script.
Here we explicitly set the exit code to the exit code from thehello-umdcommand (which we previously saved in the variableECODE).
Submitting the example
To submit the script, you can just:
- Download the script
as plain text and copy to one of the cluster login
nodes. Move it to an appropriate
directory.
- From the directory with the job script, run the command
sbatch submit.sh (replacing submit.sh with the name of the
script you downloaded). The scheduler
should return something like Submitted batch job 11274515 ---
the number will vary and is the job number for this job). The job
number can be used to reference the job in various Slurm
commands, etc. (Please always give the job
number(s) when requesting help about a job you submitted).
The job will be queued for partition specified (or defaulted to); in this example we specified the the debug partition so it will be in that partition. It should typically start running within 15 minutes or so, and finish shortly after it starts (as it is a rather trivial job).
When it finishes running, you should be able to find the files "slurm-JOBNUMBER.out" and "hello-JOBNUMBER.out" in the directory from which you ran the sbatch command; the former should contain the output of the diagnostic commands (the job number, when it started, what hosts running on, when it ended and exit code, etc), something like:
Example
Loading hello-umd/gcc/11.3.0/nompi/zen2/1.5
Loading requirement: gcc/11.3.0
Slurm job submit-hello-umd-multithreaded.sh 11381255 running on:
compute-b8-57.zaratan.umd.edu
Ntasks=1; CPU cores per task=10
On 1 nodes: compute-b8-57
Fri Apr 25 09:31:11 EDT 2025
/tmp/job.11381255
Loaded modules are:
Currently Loaded Modulefiles:
1) hpcc/zaratan(default) 3) hello-umd/gcc/11.3.0/nompi/zen2/1.5
2) gcc/11.3.0
Job finished with exit code 0
Fri Apr 25 09:31:11 EDT 2025
The "hello-JOBNUMNER.out" file should contain the
output of the actual hello-umd command, something like:
Example
hello-umd: Version 1.5
Built for compiler: gcc/11.3.0
Hello UMD from thread 0 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 2 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 8 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 4 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 5 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 7 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 3 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 1 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 9 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
Hello UMD from thread 6 of 0, task 0 of 1 (pid=934333 on host compute-b8-57.zaratan.umd.edu
In both cases, some of the specifics (e.g. job numbers, timestamps, pids, and node names) will differ from run to run. Also note that the ordering of the lines from the different threads will vary from run to run.