37
37
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
38
# SOFTWARE.
39
39
40
+ from time import time
41
+
42
+ # Capture module load time as soon as possible for other engines. The unit is seconds.
43
+ _module_start_time = time ()
44
+
40
45
import _io
41
46
import os
42
47
import sys
43
48
import types
44
- from time import time
45
-
46
49
50
+ GRAALPYTHON = sys .implementation .name == "graalpython"
51
+
52
+ # Try to use the timer with best accuracy. Unfortunately, 'monotonic_ns' is not available everywhere.
53
+ if GRAALPYTHON :
54
+ from time import monotonic_ns
55
+ monotonic_best_accuracy = monotonic_ns
56
+ UNITS_PER_SECOND = 1e9
57
+ else :
58
+ monotonic_best_accuracy = time
59
+ UNITS_PER_SECOND = 1.0
60
+
47
61
_HRULE = '-' .join (['' for i in range (80 )])
48
62
49
63
#: this function is used to pre-process the arguments as expected by the __benchmark__ and __setup__ entry points
58
72
ATTR_TEARDOWN = '__teardown__'
59
73
60
74
75
+ def get_seconds_since_startup (cur_time ):
76
+ if GRAALPYTHON and __graalpython__ .startup_nano != - 1 :
77
+ return (cur_time - __graalpython__ .startup_nano ) / UNITS_PER_SECOND
78
+ # note: the unit of _module_start_time is seconds
79
+ return cur_time / UNITS_PER_SECOND - _module_start_time
80
+
81
+
61
82
# ----------------------------------------------------------------------------------------------------------------------
62
83
#
63
84
# the CUSUM method adapted for warmup detection within a given threshold (initial iterations)
@@ -195,7 +216,7 @@ def _as_int(value):
195
216
196
217
197
218
class BenchRunner (object ):
198
- def __init__ (self , bench_file , bench_args = None , iterations = 1 , warmup = - 1 , warmup_runs = 0 ):
219
+ def __init__ (self , bench_file , bench_args = None , iterations = 1 , warmup = - 1 , warmup_runs = 0 , startup = None ):
199
220
assert isinstance (iterations , int ), \
200
221
"BenchRunner iterations argument must be an int, got %s instead" % iterations
201
222
assert isinstance (warmup , int ), \
@@ -213,6 +234,7 @@ def __init__(self, bench_file, bench_args=None, iterations=1, warmup=-1, warmup_
213
234
self .iterations = 1 if self ._run_once else _iterations
214
235
self .warmup_runs = warmup_runs if warmup_runs > 0 else 0
215
236
self .warmup = warmup if warmup > 0 else - 1
237
+ self .startup = startup
216
238
217
239
@staticmethod
218
240
def get_bench_module (bench_file ):
@@ -271,7 +293,12 @@ def run(self):
271
293
self ._call_attr (ATTR_SETUP , * args )
272
294
print ("### start benchmark ... " )
273
295
296
+ report_startup = self .startup
297
+
274
298
bench_func = self ._get_attr (ATTR_BENCHMARK )
299
+ startup_s = - 1.0
300
+ early_warmup_s = - 1.0
301
+ late_warmup_s = - 1.0
275
302
durations = []
276
303
if bench_func and hasattr (bench_func , '__call__' ):
277
304
if self .warmup_runs :
@@ -283,7 +310,14 @@ def run(self):
283
310
for iteration in range (self .iterations ):
284
311
start = time ()
285
312
bench_func (* args )
313
+ cur_time = monotonic_best_accuracy ()
286
314
duration = time () - start
315
+ if report_startup and startup_s < 0.0 and iteration == self .startup [0 ] - 1 :
316
+ startup_s = get_seconds_since_startup (cur_time )
317
+ if report_startup and early_warmup_s < 0.0 and iteration == self .startup [1 ] - 1 :
318
+ early_warmup_s = get_seconds_since_startup (cur_time )
319
+ if report_startup and late_warmup_s < 0 and iteration == self .startup [2 ] - 1 :
320
+ late_warmup_s = get_seconds_since_startup (cur_time )
287
321
durations .append (duration )
288
322
duration_str = "%.3f" % duration
289
323
self ._call_attr (ATTR_CLEANUP , * args )
@@ -311,6 +345,11 @@ def run(self):
311
345
print (_HRULE )
312
346
313
347
# summary
348
+ # We can do that only on Graalpython
349
+ if report_startup :
350
+ print ("### STARTUP at iteration: %d, duration: %.3f" % (self .startup [0 ], startup_s ))
351
+ print ("### EARLY WARMUP at iteration: %d, duration: %.3f" % (self .startup [1 ], early_warmup_s ))
352
+ print ("### LATE WARMUP at iteration: %d, duration: %.3f" % (self .startup [2 ], late_warmup_s ))
314
353
if self ._run_once :
315
354
print ("### SINGLE RUN duration: %.3f s" % durations [0 ])
316
355
else :
@@ -333,6 +372,9 @@ def run(self):
333
372
else :
334
373
print ("### WARMUP iteration not specified or could not be detected" )
335
374
375
+ if GRAALPYTHON and self .startup and __graalpython__ .startup_nano == - 1 :
376
+ print ("### NOTE: enable startup time snapshotting to increase accuracy." )
377
+
336
378
print (_HRULE )
337
379
print ("### RAW DURATIONS: %s" % str (durations ))
338
380
print (_HRULE )
@@ -342,6 +384,7 @@ def run_benchmark(args):
342
384
warmup = - 1
343
385
warmup_runs = 0
344
386
iterations = 1
387
+ startup = None
345
388
bench_file = None
346
389
bench_args = []
347
390
paths = []
@@ -367,6 +410,13 @@ def run_benchmark(args):
367
410
elif arg .startswith ("--warmup-runs" ):
368
411
warmup_runs = _as_int (arg .split ("=" )[1 ])
369
412
413
+ elif arg .startswith ('--startup' ):
414
+ try :
415
+ itrs = arg .split ("=" )[1 ].split ("," )
416
+ startup = (int (itrs [0 ]), int (itrs [1 ]), int (itrs [2 ]))
417
+ except :
418
+ raise TypeError ("incorrect argument; must be in form of '-s 1,10,100'" )
419
+
370
420
elif arg == '-p' :
371
421
i += 1
372
422
paths = args [i ].split ("," )
@@ -379,6 +429,11 @@ def run_benchmark(args):
379
429
bench_args .append (arg )
380
430
i += 1
381
431
432
+ min_required_iterations = max (startup ) if startup else 0
433
+ if startup and iterations < min_required_iterations :
434
+ print ("### WARNING: you've specified less iterations than required to measure the startup. Overriding iterations with %d" % min_required_iterations )
435
+ iterations = min_required_iterations
436
+
382
437
# set the paths if specified
383
438
print (_HRULE )
384
439
sys .path .append (os .path .split (bench_file )[0 ])
@@ -389,7 +444,7 @@ def run_benchmark(args):
389
444
else :
390
445
print ("### no extra module search paths specified" )
391
446
392
- BenchRunner (bench_file , bench_args = bench_args , iterations = iterations , warmup = warmup , warmup_runs = warmup_runs ).run ()
447
+ BenchRunner (bench_file , bench_args = bench_args , iterations = iterations , warmup = warmup , warmup_runs = warmup_runs , startup = startup ).run ()
393
448
394
449
395
450
if __name__ == '__main__' :
0 commit comments