Last active
February 25, 2020 04:42
-
-
Save JeffreySarnoff/97d213cd03588c745e32e88ee2563c3c to your computer and use it in GitHub Desktop.
BenchmarkTools: a macro to wrap `$x` as `$(Ref(x))[]`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#= | |
This material is the work of Mason Protter | |
https://github.com/JuliaCI/BenchmarkTools.jl/pull/140 | |
id(x) = x | |
a, b = 1, 2 | |
sym = :sym | |
@refd :($a) | |
:((Base.RefValue{Int64}(1))[]) | |
@btime id($a) # 0.001 ns | |
@refd @btime id($a) # 1.100 ns | |
@refd :($sym) | |
:((Base.RefValue{Symbol}(:sym)).x) | |
@btime id($sym) # 9.000 ns | |
@refd @btime id($sym) # 1.400 ns | |
@btime $a + $b # 0.024 ns | |
@refd @btime $a + $b # 1.277 ns | |
=# | |
#= | |
Sometimes, interpolating variables into very simple expressions can give the compiler | |
more information than you intended, causing it to "cheat" the benchmark by hoisting | |
the calculation out of the benchmark code. | |
``` | |
julia> a = 1; b = 2 | |
2 | |
julia> @btime $a + $b | |
0.024 ns (0 allocations: 0 bytes) | |
3 | |
``` | |
As a rule of thumb, if a benchmark reports that it took less than a nanosecond | |
to perform this hoisting probably occured. You can avoid this by referencing | |
and dereferencing the interpolated variables. | |
``` | |
julia> @btime Ref($a)[] + Ref($b)[] | |
1.524 ns (0 allocations: 0 bytes) | |
3 | |
``` | |
As this can be easily misremembered and tedious to write, so we provide a macro | |
@refd to automatically reference and dereference interpolated variables. | |
``` | |
julia> @refd @btime $a + $b | |
1.277 ns (0 allocations: 0 bytes) | |
3 | |
``` | |
Note that @refd should be appended outside the @btime statement. | |
=# | |
using BenchmarkTools | |
using MacroTools: MacroTools, prewalk, postwalk, @capture | |
walk(x, inner, outer) = outer(x) | |
walk(x::Expr, inner, outer) = outer(Expr(x.head, map(inner, x.args)...)) | |
MacroTools.postwalk(f, x) = walk(x, x -> postwalk(f, x), f) | |
function _refd(expr::Expr) | |
if expr.head == :$ | |
:($(Expr(:$, :(Ref($(expr.args...)))))[]) | |
else | |
expr | |
end | |
end | |
_refd(x) = x | |
""" | |
@refd bmacro expression | |
where `bmacro` is one of `@btime`, `@belapsed`, `@benchmark` | |
In `expression`, wraps all '$'-prefixed variables `$var` in a `Ref()`. | |
- (`$var`) is `var` prefixed with '$' | |
This prevents the compiler from omitting code that is used | |
Works with any macro that accepts interpolation | |
Example | |
julia> @btime $a + $b | |
0.024 ns (0 allocations: 0 bytes) | |
3 | |
julia> @refd @btime $a + $b | |
1.277 ns (0 allocations: 0 bytes) | |
3 | |
""" | |
macro refd(expr) | |
out = postwalk(_refd, expr) |> esc | |
end | |
#= | |
for experimentation | |
# using Ref(x).x | |
function _refx(expr::Expr) | |
if expr.head == :$ | |
:($:($(Expr(:$, :(Ref($(expr.args...)))))).x) | |
else | |
expr | |
end | |
end | |
_refx(x) = x | |
macro refx(expr) | |
out = postwalk(_refx, expr) |> esc | |
end | |
=# | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment