Skip to content

Commit ea7138d

Browse files
authored
Merge pull request #7 from tkphd/jitter
Add random jitter to sleep times
2 parents be2c1e9 + f0055bd commit ea7138d

File tree

1 file changed

+51
-10
lines changed

1 file changed

+51
-10
lines changed

amdahl/amdahl.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
import argparse
44
import json
5+
import random
56

67
from mpi4py import MPI
78

@@ -17,7 +18,12 @@
1718
parallelizable 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+
81110
def 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():
124160
def 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-
"\nTotal execution time (according to rank 0): %f seconds\n" % (end - start)
193+
"\nTotal execution time (according to rank 0): "
194+
"%f seconds\n" % (end - start)
154195
)
155196
else:
156197
do_work()

0 commit comments

Comments
 (0)