Python

serial python

The following python script (hello_world.py) is a simple script.

hello_world.py
1
print ("Hello, World!")
submit.sh
1
2
3
4
5
6
7
#!/bin/bash
#SBATCH --job-name=serial_python
#SBATCH --output=serial_python.out
#SBATCH --partition=standard

# execute Python script
srun python hello_world.py

Data Parallelism in Python

The multiprocessing module in Python provides users the ability to distribute multiple inputs to a single function over multiple processors (data parallelism). This is useful when evaluating a single function multiple times so that the computations are shared by multiple processes.

Python script

The following example is a demonstration of a simple function being distributed to multiple processor cores.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import os
import time
import multiprocessing as mproc

def myfunc(x):

    # 1 second delay
    time.sleep(1)
    xsq = x**2

    # get information on the current process and print to stdout
    proc = mproc.current_process()
    print ('{0:2}^2 = {1:4} calculated on {2} (pid {3})'.format(x, xsq, proc.name, proc.pid), flush=True)

    return xsq


#------------------------------------------------------------------------------
# USE A TIMER FOR TESTING SERIAL EXECUTION

# start timer
start = time.time()

# serial processes
result_serial = [myfunc(i) for i in range(8)]

# stop timer and calculate elapsed time
stop = time.time()
time_elapsed = stop - start
print ('\n Serial job completed in {:.2f} seconds\n'.format(time_elapsed))

#------------------------------------------------------------------------------
# USE A TIMER FOR TESTING PARALLEL EXECUTION

# get the number of processors directly from the submit.sh script
pool_size = int(os.environ['SLURM_JOB_CPUS_PER_NODE'])

# start timer
start = time.time()

# data parallel execution -- "with" statement closes pool when done
with mproc.Pool(processes=pool_size) as pool:
    result_parallel = pool.map(myfunc, range(8))

# stop timer and calculate elapsed time
stop = time.time()
time_elapsed = stop - start
print ('\n Parallel job ({} CPUs) completed in {:.2f} seconds\n'.format(pool_size, time_elapsed))

#------------------------------------------------------------------------------
# VERIFY THAT SERIAL AND PARALLEL METHODS PRODUCE SAME RESULTS

# check that serial and parallel results are equivalent
if (result_serial == result_parallel):
    print ('\n Results from serial and parallel calculation are equal!\n')

Submission script

To submit the job for parallel execution on Mercury, use a submission script similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash

#SBATCH --cpus-per-task=4      # launch 4 CPUs (cores)  per task
#SBATCH --job-name=smp-py      # name of job
#SBATCH --partition=standard   # assign the job to a specific queue
#SBATCH --output=smp-py.log    # join the output and error files

# load desired python version (check availability with 'module avail')
module load python/booth/3.6/3.6.3

# execute python script
srun python3 mp.py

In submit.sh, line #3 requests four CPU cores to be made available for the task. This job has only 1 task. You can scale up to the Max Cores value listed in the Partitions and Limits table, however we generally recommend 12 as a high value. Parallel efficiency drops as core count increases, thus high core counts should be justified by profiling your parallelism efficiency. Additionally, high core count jobs will generally be queued longer while awaiting resources to become available. The name of the job on line #4 is user-specified and should be a descriptive name subject to the following rule:

The name may be any arbitrary alphanumeric ASCII string, but
may not contain  "\n", "\t", "\r", "/", ":", "@", "\", "*",
or "?".

Line #5 assigns the job to a specific queue (for more info, see Queues and Limits). Line #10 loads the requested software in the user’s environment. More information on modules can be found in Software Modules. Job Submission

Finally, to submit the job from a login node on Mercury, simply type sbatch submit.sh at the prompt. Note that both files (mp.py and submit.sh) should be in the same directory. Sample Output Running this example produces the following output:

0^2 =    0 calculated on MainProcess (pid 30017)
1^2 =    1 calculated on MainProcess (pid 30017)
2^2 =    4 calculated on MainProcess (pid 30017)
3^2 =    9 calculated on MainProcess (pid 30017)
4^2 =   16 calculated on MainProcess (pid 30017)
5^2 =   25 calculated on MainProcess (pid 30017)
6^2 =   36 calculated on MainProcess (pid 30017)
7^2 =   49 calculated on MainProcess (pid 30017)

Serial job completed in 8.02 seconds


0^2 =    0 calculated on ForkPoolWorker-1 (pid 30019)
3^2 =    9 calculated on ForkPoolWorker-4 (pid 30022)
2^2 =    4 calculated on ForkPoolWorker-3 (pid 30021)
1^2 =    1 calculated on ForkPoolWorker-2 (pid 30020)
4^2 =   16 calculated on ForkPoolWorker-1 (pid 30019)
5^2 =   25 calculated on ForkPoolWorker-4 (pid 30022)
6^2 =   36 calculated on ForkPoolWorker-3 (pid 30021)
7^2 =   49 calculated on ForkPoolWorker-2 (pid 30020)

Parallel job (4 CPUs) completed in 2.10 seconds


Results from serial and parallel calculation are equal!

gpu python

how to