@@ -25,32 +25,53 @@ function Base.show(io::IO, b::Benchmark)
25
25
print (io, str)
26
26
end
27
27
28
+ # #####################
29
+ # compatiblity hacks #
30
+ # #####################
31
+
32
+ if VERSION >= v " 0.7.0-DEV.1139"
33
+ run_result (b:: Benchmark , p:: Parameters = b. params; kwargs... ) = Base. invokelatest (_run, b, p; kwargs... )
34
+ lineartrial (b:: Benchmark , p:: Parameters = b. params; kwargs... ) = Base. invokelatest (_lineartrial, b, p; kwargs... )
35
+ elseif VERSION < v " 0.7.0-DEV.484"
36
+ # `invokelatest` on v0.6 doesn't take keyword arguments, so we fall back
37
+ # to the `current_module` approach if we're on a Julia version where that's
38
+ # still possible.
39
+ function run_result (b:: Benchmark , p:: Parameters = b. params; kwargs... )
40
+ return eval (current_module (), :(BenchmarkTools. _run ($ (b), $ (p); $ (kwargs... ))))
41
+ end
42
+ function lineartrial (b:: Benchmark , p:: Parameters = b. params; kwargs... )
43
+ return eval (@__MODULE__ , :(BenchmarkTools. _lineartrial ($ (b), $ (p); $ (kwargs... ))))
44
+ end
45
+ else
46
+ # There's a commit gap between `current_module` deprecation and `invokelatest` keyword
47
+ # argument support. I could try to hack something together just for that gap, but it's
48
+ # probably not worth it.
49
+ error (" Congratulations, you've found the obscure range of Julia v0.7-dev commits where BenchmarkTools is really, really broken. Sorry about that :(" )
50
+ end
51
+
28
52
# ############
29
53
# execution #
30
54
# ############
31
- # Note that trials executed via `run` and `lineartrial` are always executed at the top-level
32
- # scope of the module returned by `@__MODULE__`. This is to avoid any weird quirks
33
- # when calling the experiment from within other contexts.
34
55
35
- sample (b :: Benchmark , args ... ) = error ( " no execution method defined on type $( typeof (b)) " )
36
- _run (b :: Benchmark , args ... ; kwargs ... ) = error ( " no execution method defined on type $( typeof (b)) " )
56
+ # Note that trials executed via `run` and `lineartrial` are always executed at top-level
57
+ # scope, in order to allow transfer of locally-scoped variables into benchmark scope.
37
58
38
- # return (Trial, result) tuple, where result is the result of the benchmarked expression
39
- function run_result (b:: Benchmark , p:: Parameters = b. params; kwargs... )
40
- return eval (@__MODULE__ , :(BenchmarkTools. _run ($ (b), $ (p); $ (kwargs... ))))
41
- end
59
+ # these stubs get overloaded by @benchmarkable
60
+ function sample end
61
+ function _run end
42
62
43
- Base. run (b:: Benchmark , p:: Parameters = b. params; kwargs... ) =
44
- run_result (b, p; kwargs... )[1 ]
63
+ Base. run (b:: Benchmark , p:: Parameters = b. params; kwargs... ) = run_result (b, p; kwargs... )[1 ]
45
64
46
65
function Base. run (group:: BenchmarkGroup , args... ; verbose:: Bool = false , pad = " " , kwargs... )
47
66
result = similar (group)
48
67
gcscrub () # run GC before running group, even if individual benchmarks don't manually GC
49
68
i = 1
50
69
for id in keys (group)
51
- verbose && (println (pad, " ($(i) /$(length (group)) ) benchmarking " , repr (id), " ..." ); tic ())
52
- result[id] = run (group[id], args... ; verbose = verbose, pad = pad* " " , kwargs... )
53
- verbose && (println (pad, " done (took " , toq (), " seconds)" ); i += 1 )
70
+ verbose && println (pad, " ($(i) /$(length (group)) ) benchmarking " , repr (id), " ..." )
71
+ took_seconds = @elapsed begin
72
+ result[id] = run (group[id], args... ; verbose = verbose, pad = pad* " " , kwargs... )
73
+ end
74
+ verbose && (println (pad, " done (took " , took_seconds, " seconds)" ); i += 1 )
54
75
end
55
76
return result
56
77
end
@@ -71,13 +92,13 @@ function _lineartrial(b::Benchmark, p::Parameters = b.params; maxevals = RESOLUT
71
92
return estimates[1 : completed]
72
93
end
73
94
74
- function lineartrial (b:: Benchmark , p:: Parameters = b. params; kwargs... )
75
- return eval (@__MODULE__ , :(BenchmarkTools. _lineartrial ($ (b), $ (p); $ (kwargs... ))))
95
+ function warmup (item; verbose:: Bool = true )
96
+ return run (item;
97
+ verbose = verbose, samples = 1 ,
98
+ evals = 1 , gctrial = false ,
99
+ gcsample = false )
76
100
end
77
101
78
- warmup (item; verbose:: Bool = true ) = run (item; verbose = verbose, samples = 1 , evals = 1 ,
79
- gctrial = false , gcsample = false )
80
-
81
102
# ###################
82
103
# parameter tuning #
83
104
# ###################
@@ -118,9 +139,9 @@ function tune!(group::BenchmarkGroup; verbose::Bool = false, pad = "", kwargs...
118
139
gcscrub () # run GC before running group, even if individual benchmarks don't manually GC
119
140
i = 1
120
141
for id in keys (group)
121
- verbose && ( println (pad, " ($(i) /$(length (group)) ) tuning " , repr (id), " ..." ); tic () )
122
- tune! (group[id]; verbose = verbose, pad = pad* " " , kwargs... )
123
- verbose && (println (pad, " done (took " , toq () , " seconds)" ); i += 1 )
142
+ verbose && println (pad, " ($(i) /$(length (group)) ) tuning " , repr (id), " ..." )
143
+ took_seconds = @elapsed tune! (group[id]; verbose = verbose, pad = pad* " " , kwargs... )
144
+ verbose && (println (pad, " done (took " , took_seconds , " seconds)" ); i += 1 )
124
145
end
125
146
return group
126
147
end
@@ -203,12 +224,13 @@ end
203
224
204
225
macro benchmark (args... )
205
226
_, params = prunekwargs (args... )
206
- quote
207
- tmp = @benchmarkable $ (map (esc,args)... )
208
- warmup (tmp)
209
- $ (hasevals (params) ? :() : :(tune! (tmp)))
210
- run (tmp)
211
- end
227
+ tmp = gensym ()
228
+ return esc (quote
229
+ $ tmp = $ BenchmarkTools. @benchmarkable $ (args... )
230
+ $ BenchmarkTools. warmup ($ tmp)
231
+ $ (hasevals (params) ? :() : :($ BenchmarkTools. tune! ($ tmp)))
232
+ $ BenchmarkTools. run ($ tmp)
233
+ end )
212
234
end
213
235
214
236
function benchmarkable_parts (args)
@@ -248,33 +270,33 @@ macro benchmarkable(args...)
248
270
core_vars = isa (core, Expr) ? collectvars (core) : []
249
271
out_vars = filter (var -> var in setup_vars, core_vars)
250
272
273
+ eval_module = VERSION >= v " 0.7.0-DEV.484" ? __module__ : current_module ()
274
+
251
275
# generate the benchmark definition
252
276
return esc (quote
253
- BenchmarkTools. generate_benchmark_definition (@__MODULE__ ,
254
- $ (Expr (:quote , out_vars)),
255
- $ (Expr (:quote , setup_vars)),
256
- $ (Expr (:quote , core)),
257
- $ (Expr (:quote , setup)),
258
- $ (Expr (:quote , teardown)),
259
- $ Parameters ($ (params... )))
277
+ $ BenchmarkTools. generate_benchmark_definition ($ (eval_module) ,
278
+ $ (Expr (:quote , out_vars)),
279
+ $ (Expr (:quote , setup_vars)),
280
+ $ (Expr (:quote , core)),
281
+ $ (Expr (:quote , setup)),
282
+ $ (Expr (:quote , teardown)),
283
+ $ Parameters ($ (params... )))
260
284
end )
261
285
end
262
286
263
287
# `eval` an expression that forcibly defines the specified benchmark at
264
- # top-level of `eval_module`, hopefully ensuring that the "location" of the benchmark's
265
- # definition will not be a factor for the sake of performance testing .
288
+ # top-level in order to allow transfer of locally-scoped variables into
289
+ # benchmark scope .
266
290
#
267
291
# The double-underscore-prefixed variable names are not particularly hygienic - it's
268
292
# possible for them to conflict with names used in the setup or teardown expressions.
269
293
# A more robust solution would be preferable.
270
- function generate_benchmark_definition (eval_module, out_vars, setup_vars,
271
- core, setup, teardown, params)
294
+ function generate_benchmark_definition (eval_module, out_vars, setup_vars, core, setup, teardown, params)
272
295
id = Expr (:quote , gensym (" benchmark" ))
273
296
corefunc = gensym (" core" )
274
297
samplefunc = gensym (" sample" )
275
298
signature = Expr (:call , corefunc, setup_vars... )
276
299
if length (out_vars) == 0
277
- # returns = :(return $(Expr(:tuple, setup_vars...)))
278
300
invocation = signature
279
301
core_body = core
280
302
elseif length (out_vars) == 1
@@ -286,9 +308,9 @@ function generate_benchmark_definition(eval_module, out_vars, setup_vars,
286
308
invocation = :($ (Expr (:tuple , out_vars... )) = $ (signature))
287
309
core_body = :($ (core); $ (returns))
288
310
end
289
- eval (eval_module, quote
311
+ return eval (eval_module, quote
290
312
@noinline $ (signature) = begin $ (core_body) end
291
- @noinline function $ (samplefunc)(__params:: BenchmarkTools.Parameters )
313
+ @noinline function $ (samplefunc)(__params:: $ BenchmarkTools.Parameters )
292
314
$ (setup)
293
315
__evals = __params. evals
294
316
__gc_start = Base. gc_num ()
@@ -308,31 +330,31 @@ function generate_benchmark_definition(eval_module, out_vars, setup_vars,
308
330
__evals))
309
331
return __time, __gctime, __memory, __allocs, __return_val
310
332
end
311
- function BenchmarkTools. sample (b:: BenchmarkTools.Benchmark{$(id)} ,
312
- p:: BenchmarkTools.Parameters = b. params)
333
+ function $ BenchmarkTools. sample (b:: $ BenchmarkTools.Benchmark{$(id)} ,
334
+ p:: $ BenchmarkTools.Parameters = b. params)
313
335
return $ (samplefunc)(p)
314
336
end
315
- function BenchmarkTools. _run (b:: BenchmarkTools.Benchmark{$(id)} ,
316
- p:: BenchmarkTools.Parameters ;
317
- verbose = false , pad = " " , kwargs... )
318
- params = BenchmarkTools. Parameters (p; kwargs... )
337
+ function $ BenchmarkTools. _run (b:: $ BenchmarkTools.Benchmark{$(id)} ,
338
+ p:: $ BenchmarkTools.Parameters ;
339
+ verbose = false , pad = " " , kwargs... )
340
+ params = $ BenchmarkTools. Parameters (p; kwargs... )
319
341
@assert params. seconds > 0.0 " time limit must be greater than 0.0"
320
- params. gctrial && BenchmarkTools. gcscrub ()
342
+ params. gctrial && $ BenchmarkTools. gcscrub ()
321
343
start_time = time ()
322
- trial = BenchmarkTools. Trial (params)
323
- params. gcsample && BenchmarkTools. gcscrub ()
344
+ trial = $ BenchmarkTools. Trial (params)
345
+ params. gcsample && $ BenchmarkTools. gcscrub ()
324
346
s = $ (samplefunc)(params)
325
347
push! (trial, s[1 : end - 1 ]. .. )
326
348
return_val = s[end ]
327
349
iters = 2
328
350
while (time () - start_time) < params. seconds && iters ≤ params. samples
329
- params. gcsample && BenchmarkTools. gcscrub ()
351
+ params. gcsample && $ BenchmarkTools. gcscrub ()
330
352
push! (trial, $ (samplefunc)(params)[1 : end - 1 ]. .. )
331
353
iters += 1
332
354
end
333
355
return sort! (trial), return_val
334
356
end
335
- BenchmarkTools. Benchmark {$(id)} ($ (params))
357
+ $ BenchmarkTools. Benchmark {$(id)} ($ (params))
336
358
end )
337
359
end
338
360
@@ -356,7 +378,9 @@ is the *minimum* elapsed time measured during the benchmark.
356
378
"""
357
379
macro belapsed (args... )
358
380
b = Expr (:macrocall , Symbol (" @benchmark" ), map (esc, args)... )
359
- :(time (minimum ($ b))/ 1e9 )
381
+ return esc (quote
382
+ $ BenchmarkTools. time ($ BenchmarkTools. minimum ($ BenchmarkTools. @benchmark $ (args... )))/ 1e9
383
+ end )
360
384
end
361
385
362
386
"""
@@ -374,16 +398,21 @@ is the *minimum* elapsed time measured during the benchmark.
374
398
"""
375
399
macro btime (args... )
376
400
_, params = prunekwargs (args... )
377
- quote
378
- tmp = @benchmarkable $ (map (esc,args)... )
379
- warmup (tmp)
380
- $ (hasevals (params) ? :() : :(tune! (tmp)))
381
- b, val = run_result (tmp)
382
- bmin = minimum (b)
383
- a = allocs (bmin)
384
- println (" " , prettytime (time (bmin)),
385
- " ($a allocation" , a == 1 ? " " : " s" , " : " ,
386
- prettymemory (memory (bmin)), " )" )
387
- val
388
- end
401
+ bench, trial, result = gensym (), gensym (), gensym ()
402
+ trialmin, trialallocs = gensym (), gensym ()
403
+ tune_phase = hasevals (params) ? :() : :($ BenchmarkTools. tune! ($ bench))
404
+ return esc (quote
405
+ $ bench = $ BenchmarkTools. @benchmarkable $ (args... )
406
+ $ BenchmarkTools. warmup ($ bench)
407
+ $ tune_phase
408
+ $ trial, $ result = $ BenchmarkTools. run_result ($ bench)
409
+ $ trialmin = $ BenchmarkTools. minimum ($ trial)
410
+ $ trialallocs = $ BenchmarkTools. allocs ($ trialmin)
411
+ println (" " ,
412
+ $ BenchmarkTools. prettytime ($ BenchmarkTools. time ($ trialmin)),
413
+ " (" , $ trialallocs , " allocation" ,
414
+ $ trialallocs == 1 ? " " : " s" , " : " ,
415
+ $ BenchmarkTools. prettymemory ($ BenchmarkTools. memory ($ trialmin)), " )" )
416
+ $ result
417
+ end )
389
418
end
0 commit comments