2
2
import sys
3
3
import argparse
4
4
import json
5
+ import random
5
6
6
7
from mpi4py import MPI
7
8
17
18
parallelizable proportion.
18
19
"""
19
20
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 ):
21
27
# How many MPI ranks (cores) are we?
22
28
size = comm .Get_size ()
23
29
# 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
35
41
(1.0 - parallel_proportion ) + parallel_proportion / size
36
42
)
37
43
44
+ if not exact :
45
+ serial_sleep_time = random_jitter (serial_sleep_time )
46
+
38
47
suffix = "" if size == 1 else "s"
39
48
40
49
if not terse :
41
50
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 "
43
52
" which should take %f seconds with %f parallel"
44
53
" proportion of the workload.\n \n "
45
54
% (
@@ -60,16 +69,21 @@ def do_work(work_time=30, parallel_proportion=0.8, comm=MPI.COMM_WORLD, terse=Fa
60
69
else :
61
70
parallel_sleep_time = None
62
71
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)
66
75
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
+
67
80
terse = comm .bcast (terse , root = 0 )
68
81
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)
70
83
if not terse :
71
84
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 "
73
87
"%f seconds.\n " % (rank , size , name , parallel_sleep_time )
74
88
)
75
89
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
78
92
return (size , serial_sleep_time , parallel_sleep_time )
79
93
80
94
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
+
81
110
def parse_command_line ():
82
111
# Initialize our argument parser
83
112
parser = argparse .ArgumentParser ()
@@ -107,6 +136,13 @@ def parse_command_line():
107
136
default = False ,
108
137
help = "Enable terse output" ,
109
138
)
139
+ parser .add_argument (
140
+ "-e" ,
141
+ "--exact" ,
142
+ action = 'store_true' ,
143
+ default = False ,
144
+ help = "Disable random jitter" ,
145
+ )
110
146
# Read arguments from command line
111
147
args = parser .parse_args ()
112
148
if not args .work_seconds > 0 :
@@ -124,6 +160,8 @@ def parse_command_line():
124
160
def amdahl ():
125
161
"""Amdahl's law illustrator (with fake work)"""
126
162
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 )
127
165
# Only the root process handles the command line arguments
128
166
if rank == 0 :
129
167
# Start a clock to measure total time
@@ -132,8 +170,10 @@ def amdahl():
132
170
args = parse_command_line ()
133
171
134
172
(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
137
177
)
138
178
end = time .time ()
139
179
if args .terse :
@@ -150,7 +190,8 @@ def amdahl():
150
190
)
151
191
else :
152
192
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 )
154
195
)
155
196
else :
156
197
do_work ()
0 commit comments