22import sys
33import argparse
44import json
5+ import random
56
67from mpi4py import MPI
78
1718parallelizable proportion.
1819"""
1920
20- def do_work (work_time = 30 , parallel_proportion = 0.8 , comm = MPI .COMM_WORLD , terse = False ):
21+
22+ def do_work (work_time = 30 ,
23+ parallel_proportion = 0.8 ,
24+ comm = MPI .COMM_WORLD ,
25+ terse = False ,
26+ exact = False ):
2127 # How many MPI ranks (cores) are we?
2228 size = comm .Get_size ()
2329 # Who am I in that set of ranks?
@@ -35,11 +41,14 @@ def do_work(work_time=30, parallel_proportion=0.8, comm=MPI.COMM_WORLD, terse=Fa
3541 (1.0 - parallel_proportion ) + parallel_proportion / size
3642 )
3743
44+ if not exact :
45+ serial_sleep_time = random_jitter (serial_sleep_time )
46+
3847 suffix = "" if size == 1 else "s"
3948
4049 if not terse :
4150 sys .stdout .write (
42- "Doing %f seconds of 'work' on %s processor %s,\n "
51+ "Doing %f seconds of 'work' on %s processor%s,\n "
4352 " which should take %f seconds with %f parallel"
4453 " proportion of the workload.\n \n "
4554 % (
@@ -60,16 +69,21 @@ def do_work(work_time=30, parallel_proportion=0.8, comm=MPI.COMM_WORLD, terse=Fa
6069 else :
6170 parallel_sleep_time = None
6271
63- # Tell all processes how much work they need to do using 'bcast' to broadcast
64- # (this also creates an implicit barrier, blocking processes until they receive
65- # the value)
72+ # Tell all processes how much work they need to do using 'bcast' to
73+ # broadcast (this also creates an implicit barrier, blocking processes
74+ # until they receive the value)
6675 parallel_sleep_time = comm .bcast (parallel_sleep_time , root = 0 )
76+
77+ if not exact :
78+ parallel_sleep_time = random_jitter (parallel_sleep_time )
79+
6780 terse = comm .bcast (terse , root = 0 )
6881
69- # This is where everyone pretends to do work (while really we are just sleeping)
82+ # This is where everyone pretends to do work (really we are just sleeping)
7083 if not terse :
7184 sys .stdout .write (
72- " Hello, World! I am process %d of %d on %s. I will do parallel 'work' for "
85+ " Hello, World! "
86+ "I am process %d of %d on %s. I will do parallel 'work' for "
7387 "%f seconds.\n " % (rank , size , name , parallel_sleep_time )
7488 )
7589 time .sleep (parallel_sleep_time )
@@ -78,6 +92,21 @@ def do_work(work_time=30, parallel_proportion=0.8, comm=MPI.COMM_WORLD, terse=Fa
7892 return (size , serial_sleep_time , parallel_sleep_time )
7993
8094
95+ def random_jitter (x , sigma = 0.2 ):
96+ """
97+ Apply a random offset of ±20% to a value
98+ """
99+ # Make sure sigma is between 0 and 1
100+ if sigma < 0 or sigma > 1 :
101+ sys .stdout .write (
102+ "Illegal value for sigma (%f), should be a float between 0 and 1!\n " % sigma
103+ "Using 0.2 instead..."
104+ sigma = 0.2
105+ # random() returns a float between 0 and 1, map between -sigma and +sigma
106+ jitter_percent = sigma * ((random .random () * 2 ) - 1 )
107+ return (1 + jitter_percent ) * x
108+
109+
81110def parse_command_line ():
82111 # Initialize our argument parser
83112 parser = argparse .ArgumentParser ()
@@ -107,6 +136,13 @@ def parse_command_line():
107136 default = False ,
108137 help = "Enable terse output" ,
109138 )
139+ parser .add_argument (
140+ "-e" ,
141+ "--exact" ,
142+ action = 'store_true' ,
143+ default = False ,
144+ help = "Disable random jitter" ,
145+ )
110146 # Read arguments from command line
111147 args = parser .parse_args ()
112148 if not args .work_seconds > 0 :
@@ -124,6 +160,8 @@ def parse_command_line():
124160def amdahl ():
125161 """Amdahl's law illustrator (with fake work)"""
126162 rank = MPI .COMM_WORLD .Get_rank ()
163+ # Ensure that all ranks use a guaranteed unique seed when generating random numbers
164+ random .seed (int (time .time ()) + rank )
127165 # Only the root process handles the command line arguments
128166 if rank == 0 :
129167 # Start a clock to measure total time
@@ -132,8 +170,10 @@ def amdahl():
132170 args = parse_command_line ()
133171
134172 (nproc , serial_work , parallel_work ) = do_work (
135- work_time = args .work_seconds , parallel_proportion = args .parallel_proportion ,
136- terse = args .terse
173+ work_time = args .work_seconds ,
174+ parallel_proportion = args .parallel_proportion ,
175+ terse = args .terse ,
176+ exact = args .exact
137177 )
138178 end = time .time ()
139179 if args .terse :
@@ -150,7 +190,8 @@ def amdahl():
150190 )
151191 else :
152192 sys .stdout .write (
153- "\n Total execution time (according to rank 0): %f seconds\n " % (end - start )
193+ "\n Total execution time (according to rank 0): "
194+ "%f seconds\n " % (end - start )
154195 )
155196 else :
156197 do_work ()
0 commit comments