@@ -9,6 +9,7 @@ gcscrub() = (GC.gc(); GC.gc(); GC.gc(); GC.gc())
99
1010mutable struct Benchmark
1111 samplefunc
12+ quote_vals
1213 params:: Parameters
1314end
1415
@@ -95,13 +96,13 @@ function _run(b::Benchmark, p::Parameters; verbose = false, pad = "", kwargs...)
9596 start_time = Base. time ()
9697 trial = Trial (params)
9798 params. gcsample && gcscrub ()
98- s = b. samplefunc (params)
99+ s = b. samplefunc (b . quote_vals, params)
99100 push! (trial, s[1 : end - 1 ]. .. )
100101 return_val = s[end ]
101102 iters = 2
102103 while (Base. time () - start_time) < params. seconds && iters ≤ params. samples
103104 params. gcsample && gcscrub ()
104- push! (trial, b. samplefunc (params)[1 : end - 1 ]. .. )
105+ push! (trial, b. samplefunc (b . quote_vals, params)[1 : end - 1 ]. .. )
105106 iters += 1
106107 end
107108 return trial, return_val
@@ -157,7 +158,7 @@ function _lineartrial(b::Benchmark, p::Parameters = b.params; maxevals = RESOLUT
157158 for evals in eachindex (estimates)
158159 params. gcsample && gcscrub ()
159160 params. evals = evals
160- estimates[evals] = first (b. samplefunc (params))
161+ estimates[evals] = first (b. samplefunc (b . quote_vals, params))
161162 completed += 1
162163 ((time () - start_time) > params. seconds) && break
163164 end
@@ -305,18 +306,22 @@ function collectvars(ex::Expr, vars::Vector{Symbol} = Symbol[])
305306 return vars
306307end
307308
308- function quasiquote! (ex:: Expr , vars:: Vector{Expr} )
309+ """
310+ quasiquote!(expr::Expr, vars::Vector{Symbol}, vals::Vector{Expr})
311+
312+ Replace every interpolated value in `expr` with a placeholder variable and
313+ store the resulting variable / value pairings in `vars` and `vals`.
314+ """
315+ quasiquote! (ex, _... ) = ex
316+ function quasiquote! (ex:: Expr , vars:: Vector{Symbol} , vals:: Vector{Expr} )
309317 if ex. head === :($ )
310- lhs = ex. args[1 ]
311- rhs = isa (lhs, Symbol) ? gensym (lhs) : gensym ( )
312- push! (vars, Expr (:( = ), rhs, ex) )
313- return rhs
318+ var = isa ( ex. args[1 ], Symbol) ? gensym (ex . args[ 1 ]) : gensym ()
319+ push! (vars, var )
320+ push! (vals, ex )
321+ return var
314322 elseif ex. head != = :quote
315323 for i in 1 : length (ex. args)
316- arg = ex. args[i]
317- if isa (arg, Expr)
318- ex. args[i] = quasiquote! (arg, vars)
319- end
324+ ex. args[i] = quasiquote! (ex. args[i], vars, vals)
320325 end
321326 end
322327 return ex
@@ -410,15 +415,11 @@ function benchmarkable_parts(args)
410415 end
411416 deleteat! (params, delinds)
412417
413- if isa (core, Expr)
414- quote_vars = Expr[]
415- core = quasiquote! (core, quote_vars)
416- if ! isempty (quote_vars)
417- setup = Expr (:block , setup, quote_vars... )
418- end
419- end
418+ quote_vars = Symbol[]
419+ quote_vals = Expr[]
420+ core = quasiquote! (core, quote_vars, quote_vals)
420421
421- return core, setup, teardown, params
422+ return core, setup, teardown, quote_vars, quote_vals, params
422423end
423424
424425"""
@@ -428,7 +429,7 @@ Create a `Benchmark` instance for the given expression. `@benchmarkable`
428429has similar syntax with `@benchmark`. See also [`@benchmark`](@ref).
429430"""
430431macro benchmarkable (args... )
431- core, setup, teardown, params = benchmarkable_parts (args)
432+ core, setup, teardown, quote_vars, quote_vals, params = benchmarkable_parts (args)
432433 map! (esc, params, params)
433434
434435 # extract any variable bindings shared between the core and setup expressions
@@ -441,6 +442,8 @@ macro benchmarkable(args...)
441442 generate_benchmark_definition ($ __module__,
442443 $ (Expr (:quote , out_vars)),
443444 $ (Expr (:quote , setup_vars)),
445+ $ (Expr (:quote , quote_vars)),
446+ $ (esc (Expr (:vect ,Expr .(:quote , quote_vals)... ))),
444447 $ (esc (Expr (:quote , core))),
445448 $ (esc (Expr (:quote , setup))),
446449 $ (esc (Expr (:quote , teardown))),
@@ -455,14 +458,14 @@ end
455458# The double-underscore-prefixed variable names are not particularly hygienic - it's
456459# possible for them to conflict with names used in the setup or teardown expressions.
457460# A more robust solution would be preferable.
458- function generate_benchmark_definition (eval_module, out_vars, setup_vars, core, setup, teardown, params)
461+ function generate_benchmark_definition (eval_module, out_vars, setup_vars, quote_vars, quote_vals, core, setup, teardown, params)
459462 @nospecialize
460463 corefunc = gensym (" core" )
461464 samplefunc = gensym (" sample" )
462- type_vars = [gensym () for i in 1 : length (setup_vars)]
463- signature = Expr (:call , corefunc, setup_vars... )
465+ type_vars = [gensym () for i in 1 : length (quote_vars) + length ( setup_vars)]
466+ signature = Expr (:call , corefunc, quote_vars ... , setup_vars... )
464467 signature_def = Expr (:where , Expr (:call , corefunc,
465- [Expr (:(:: ), setup_var, type_var ) for (setup_var, type_var ) in zip (setup_vars, type_vars)]. .. )
468+ [Expr (:(:: ), var, type ) for (var, type ) in zip ([quote_vars; setup_vars] , type_vars)]. .. )
466469 , type_vars... )
467470 if length (out_vars) == 0
468471 invocation = signature
@@ -478,7 +481,7 @@ function generate_benchmark_definition(eval_module, out_vars, setup_vars, core,
478481 end
479482 return Core. eval (eval_module, quote
480483 @noinline $ (signature_def) = begin $ (core_body) end
481- @noinline function $ (samplefunc)(__params:: $BenchmarkTools.Parameters )
484+ @noinline function $ (samplefunc)($ ( Expr ( :tuple , quote_vars ... )), __params:: $BenchmarkTools.Parameters )
482485 $ (setup)
483486 __evals = __params. evals
484487 __gc_start = Base. gc_num ()
@@ -498,7 +501,7 @@ function generate_benchmark_definition(eval_module, out_vars, setup_vars, core,
498501 __evals))
499502 return __time, __gctime, __memory, __allocs, __return_val
500503 end
501- $ BenchmarkTools. Benchmark ($ (samplefunc), $ (params))
504+ $ BenchmarkTools. Benchmark ($ (samplefunc), $ (quote_vals), $ ( params))
502505 end )
503506end
504507
0 commit comments