Skip to content

Commit 70d083e

Browse files
authored
Document the practice of referencing and dereferencing variables to stop constant prop (#140)
Co-authored-by: Mason Protter <[email protected]>
1 parent 451b070 commit 70d083e

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ julia> @btime inv(rand(3,3)); # the rand(3,3) call is included in the benchm
7878
1.295 μs (11 allocations: 2.47 KiB)
7979
```
8080

81+
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
82+
```julia
83+
julia> a = 1; b = 2
84+
2
85+
86+
julia> @btime $a + $b
87+
0.024 ns (0 allocations: 0 bytes)
88+
3
89+
```
90+
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
91+
```julia
92+
julia> @btime $(Ref(a))[] + $(Ref(b))[]
93+
1.277 ns (0 allocations: 0 bytes)
94+
3
95+
```
96+
8197
As described the [manual](doc/manual.md), the BenchmarkTools package supports many other features, both for additional output and for more fine-grained control over the benchmarking process.
8298

8399
## Why does this package exist?

doc/manual.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ Note that the `setup` and `teardown` phases are **executed for each sample, not
275275
### Understanding compiler optimizations
276276

277277
It's possible for LLVM and Julia's compiler to perform optimizations on `@benchmarkable` expressions. In some cases, these optimizations can elide a computation altogether, resulting in unexpectedly "fast" benchmarks. For example, the following expression is non-allocating:
278-
279278
```julia
280279
julia> @benchmark (view(a, 1:2, 1:2); 1) setup=(a = rand(3, 3))
281280
BenchmarkTools.Trial:
@@ -311,7 +310,23 @@ BenchmarkTools.Trial:
311310

312311
The key point here is that these two benchmarks measure different things, even though their code is similar. In the first example, Julia was able to optimize away `view(a, 1:2, 1:2)` because it could prove that the value wasn't being returned and `a` wasn't being mutated. In the second example, the optimization is not performed because `view(a, 1:2, 1:2)` is a return value of the benchmark expression.
313312

314-
In conclusion, BenchmarkTools will faithfully report the performance of the exact code that you provide to it, including any compiler optimizations that might happen to elide the code completely. It's up to you to design benchmarks which actually exercise the code you intend to exercise.
313+
BenchmarkTools will faithfully report the performance of the exact code that you provide to it, including any compiler optimizations that might happen to elide the code completely. It's up to you to design benchmarks which actually exercise the code you intend to exercise.
314+
315+
A common place julia's optimizer may cause a benchmark to not measure what a user thought it was measuring is simple operations where all values are known at compile time. Suppose you wanted to measure the time it takes to add together two integers:
316+
```julia
317+
julia> a = 1; b = 2
318+
2
319+
320+
julia> @btime $a + $b
321+
0.024 ns (0 allocations: 0 bytes)
322+
3
323+
```
324+
in this case julia was able to use the properties of `+(::Int, ::Int)` to know that it could safely replace `$a + $b` with `3` at compile time. We can stop the optimizer from doing this by referencing and dereferencing the interpolated variables
325+
```julia
326+
julia> @btime $(Ref(a))[] + $(Ref(b))[]
327+
1.277 ns (0 allocations: 0 bytes)
328+
3
329+
```
315330

316331
# Handling benchmark results
317332

0 commit comments

Comments
 (0)