Consider a tuple t = (1,1,1) for example. 1/t[1] and 1/1 are lowered to the same code:
julia> @code_lowered 1/t[1]
CodeInfo(:(begin
nothing
return (Base.float)(x) / (Base.float)(y)
end))
julia> @code_lowered 1/1
CodeInfo(:(begin
nothing
return (Base.float)(x) / (Base.float)(y)
end))
However, 1/t[1] uses an allocation, whereas 1/1 doesn't. As a result, 1/t[1] is much much slower than 1/1:
julia> @benchmark 1/t[1]
BenchmarkTools.Trial:
memory estimate: 16.00 bytes
allocs estimate: 1
--------------
minimum time: 78.511 ns (0.00% GC)
median time: 80.084 ns (0.00% GC)
mean time: 90.435 ns (2.50% GC)
maximum time: 4.507 渭s (97.09% GC)
--------------
samples: 10000
evals/sample: 968
time tolerance: 5.00%
memory tolerance: 1.00%
julia> @benchmark 1/1
BenchmarkTools.Trial:
memory estimate: 0.00 bytes
allocs estimate: 0
--------------
minimum time: 2.325 ns (0.00% GC)
median time: 2.329 ns (0.00% GC)
mean time: 2.669 ns (0.00% GC)
maximum time: 2.010 渭s (0.00% GC)
--------------
samples: 10000
evals/sample: 1000
time tolerance: 5.00%
memory tolerance: 1.00%
Among basic arithmetic operations, only division uses an allocation. Addition (1+t[1]), subtraction (1-t[1]), multiplication (1*t[1]) do not use any allocation. I don't understand why 1/t[1] needs an allocation.
Is this something that needs to be fixed, or something expected?
Here is the versioninfo() result:
julia> versioninfo()
Julia Version 0.6.0-dev.1738
Commit 309657f* (2016-12-28 18:43 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin16.0.0)
CPU: Intel(R) Core(TM)2 Duo CPU T9900 @ 3.06GHz
WORD_SIZE: 64
BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Penryn)
LAPACK: libopenblas64_
LIBM: libopenlibm
LLVM: libLLVM-3.7.1 (ORCJIT, penryn)
Don't benchmark in global scope.
Is there a manifestation of this within a larger computation? It can be difficult to put a single operation under the microscope sometimes.
@KristofferC Thanks. I read somewhere that to measure the correct number of allocations, we should either wrap the code within a function and call it with @time, or simply call the code with @benchmark, so I thought I didn't have to wrap the code as long as I use @benchmark...
@JeffBezanson I observed that some portion of my code was using too many allocations than I expected. A few experiments led me to believe that the extra allocation reported above was the culprit, but apparently I was wrong. I will look into the problem again and report back if I still don't understand why it uses too many allocations.
You can interpolate variables into the expression after @benchmark which can help with having variables in global scope, ex:
julia> a = 3
julia> @benchmark sin(a)
memory estimate: 16.00 bytes
allocs estimate: 1
minimum time: 30.00 ns (0.00% GC)
julia> @benchmark sin($a)
BenchmarkTools.Trial:
memory estimate: 0.00 bytes
allocs estimate: 0
minimum time: 13.00 ns (0.00% GC)
Most helpful comment
You can interpolate variables into the expression after
@benchmarkwhich can help with having variables in global scope, ex: