-
Notifications
You must be signed in to change notification settings - Fork 1
/
DB12.py
executable file
·232 lines (178 loc) · 7.55 KB
/
DB12.py
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#!/usr/bin/python
########################################################################
# File : DIRACbenchmark.py
# Author : Andrew McNab
########################################################################
""" DIRAC Benchmark 2012 by Ricardo Graciani, and wrapper functions to
run multiple copies in parallel by Andrew McNab.
This file (DIRACbenchmark.py) is intended to be the ultimate upstream
shared by different users of the DIRAC Benchmark 2012 (DB12). The
canonical version can be found at https://github.com/DIRACGrid/DB12
This script can either be imported or run from the command line:
./DIRACbenchmark.py NUMBER
where NUMBER gives the number of benchmark processes to run in parallel.
Run ./DIRACbenchmark.py help to see more options.
"""
import os
import sys
import random
import urllib
import multiprocessing
version = '00.03 DB12'
def singleDiracBenchmark( iterations = 1, extraIteration = False ):
""" Get Normalized Power of one CPU in DIRAC Benchmark 2012 units (DB12)
"""
# This number of iterations corresponds to 1kHS2k.seconds, i.e. 250 HS06 seconds
n = int( 1000 * 1000 * 12.5 )
calib = 250.0
m = 0
m2 = 0
p = 0
p2 = 0
# Do one iteration extra to allow CPUs with variable speed (we ignore zeroth iteration)
# Possibly do one extra iteration to avoid tail effects when copies run in parallel
for i in range( iterations + 1 + (2 if extraIteration else 0)):
if i == 1:
start = os.times()
# Now the iterations
for _j in xrange( n ):
t = random.normalvariate( 10, 1 )
m += t
m2 += t * t
p += t
p2 += t * t
if i == iterations:
end = os.times()
cput = sum( end[:4] ) - sum( start[:4] )
wall = end[4] - start[4]
if not cput:
return None
# Return DIRAC-compatible values
return { 'CPU' : cput, 'WALL' : wall, 'NORM' : calib * iterations / cput, 'UNIT' : 'DB12' }
def singleDiracBenchmarkProcess( resultObject, iterations = 1, extraIteration = False ):
""" Run singleDiracBenchmark() in a multiprocessing friendly way
"""
benchmarkResult = singleDiracBenchmark( iterations = iterations, extraIteration = extraIteration )
if not benchmarkResult or 'NORM' not in benchmarkResult:
return None
# This makes it easy to use with multiprocessing.Process
resultObject.value = benchmarkResult['NORM']
def multipleDiracBenchmark( copies = 1, iterations = 1, extraIteration = False ):
""" Run multiple copies of the DIRAC Benchmark in parallel
"""
processes = []
results = []
# Set up all the subprocesses
for i in range( copies ):
results.append( multiprocessing.Value('d', 0.0) )
processes.append( multiprocessing.Process( target = singleDiracBenchmarkProcess, args = ( results[i], iterations, extraIteration) ) )
# Start them all off at the same time
for p in processes:
p.start()
# Wait for them all to finish
for p in processes:
p.join()
raw = []
product = 1.0
for result in results:
raw.append( result.value )
product *= result.value
raw.sort()
# Return the list of raw results and various averages
return { 'raw' : raw,
'copies' : copies,
'sum' : sum(raw),
'arithmetic_mean' : sum(raw)/copies,
'geometric_mean' : product ** (1.0 / copies),
'median' : raw[(copies-1) / 2] }
def wholenodeDiracBenchmark( copies = None, iterations = 1, extraIteration = False ):
""" Run as many copies as needed to occupy the whole machine
"""
# Try $MACHINEFEATURES first if not given by caller
if not copies and 'MACHINEFEATURES' in os.environ:
try:
copies = int( urllib.urlopen( os.environ['MACHINEFEATURES'] + '/total_cpu' ).read() )
except:
pass
# If not given by caller or $MACHINEFEATURES/total_cpu then just count CPUs
if not copies:
try:
copies = multiprocessing.cpu_count()
except:
copies = 1
return multipleDiracBenchmark( copies = copies, iterations = iterations, extraIteration = extraIteration )
def jobslotDiracBenchmark( copies = None, iterations = 1, extraIteration = False ):
""" Run as many copies as needed to occupy the job slot
"""
# Try $JOBFEATURES first if not given by caller
if not copies and 'JOBFEATURES' in os.environ:
try:
copies = int( urllib.urlopen( os.environ['JOBFEATURES'] + '/allocated_cpu' ).read() )
except:
pass
# If not given by caller or $JOBFEATURES/allocated_cpu then just run one copy
if not copies:
copies = 1
return multipleDiracBenchmark( copies = copies, iterations = iterations, extraIteration = extraIteration )
#
# If we run as a command
#
if __name__ == "__main__":
helpString = """DIRACbenchmark.py [--iterations ITERATIONS] [--extra-iteration]
[COPIES|single|wholenode|jobslot|version|help]
Uses the functions within DIRACbenchmark.py to run the DB12 benchmark from the
command line.
By default one benchmarking iteration is run, in addition to the initial
iteration which DB12 runs and ignores to avoid ramp-up effects at the start.
The number of benchmarking iterations can be increased using the --iterations
option. An additional final iteration which is also ignored can be added with
the --extra-iteration option, to avoid tail effects.
The COPIES (ie an integer) argument causes multiple copies of the benchmark to
be run in parallel. The tokens "wholenode", "jobslot" and "single" can be
given instead to use $MACHINEFEATURES/total_cpu, $JOBFEATURES/allocated_cpu,
or 1 as the number of copies respectively. If $MACHINEFEATURES/total_cpu is
not available, then the number of (logical) processors visible to the
operating system is used.
Unless the token "single" is used, the script prints the following results to
two lines on stdout:
COPIES SUM ARITHMETIC-MEAN GEOMETRIC-MEAN MEDIAN
RAW-RESULTS
The tokens "version" and "help" print information about the script.
The source code of DIRACbenchmark.py provides examples of how the functions
within DIRACbenchmark.py can be used by other Python programs.
DIRACbenchmark.py is distributed from https://github.com/DIRACGrid/DB12
"""
copies = None
iterations = 1
extraIteration = False
for arg in sys.argv[1:]:
if arg.startswith('--iterations='):
iterations = int(arg[13:])
elif arg == '--extra-iteration':
extraIteration = True
elif arg == '--help' or arg == 'help':
print(helpString)
sys.exit(0)
elif not arg.startswith('--'):
copies = arg
print(iterations, copies)
if copies == 'version':
print(version)
sys.exit(0)
if copies is None or copies == 'single':
print(singleDiracBenchmark()['NORM'])
sys.exit(0)
if copies == 'wholenode':
result = wholenodeDiracBenchmark( iterations = iterations, extraIteration = extraIteration )
print(result['copies'],result['sum'],result['arithmetic_mean'],result['geometric_mean'],result['median'])
print(' '.join([str(i) for i in result['raw']]))
sys.exit(0)
if copies == 'jobslot':
result = jobslotDiracBenchmark( iterations = iterations, extraIteration = extraIteration )
print(result['copies'],result['sum'],result['arithmetic_mean'],result['geometric_mean'],result['median'])
print(' '.join([str(i) for i in result['raw']]))
sys.exit(0)
result = multipleDiracBenchmark( copies = int(copies), iterations = iterations, extraIteration = extraIteration )
print(result['copies'],result['sum'],result['arithmetic_mean'],result['geometric_mean'],result['median'])
print(' '.join([str(i) for i in result['raw']]))
sys.exit(0)