If you wrap it all in a function, it doesn’t show any allocations neither
julia> function foo()
nt = (a=1, b=2, c=3)
t = (:b, :c)
nt[t]
end
foo (generic function with 1 method)
julia> @btime foo()
0.791 ns (0 allocations: 0 bytes)
(b = 2, c = 3)
As @bertschi said, the difference is if it is constant folding or not.
What in particular the problem is, is unless the values of t are constant folded,
we can’t know the return type of the expression.
Since if t is (:a,) it is @NamedTuple{a::Int64} vs if it is (:a, :b) it is: @NamedTuple{a::Int64, b::Int64} etc.
And that means it is a type unstable expression and the compiler needs to allocate memory on the heap for this object of unknown size.
Where as if we do know the value, then it can be allocated on the stack (which doesn’t show up as an allocation at all).
This constant folding can be seen in @Albert_de_montserrat 's answer, because constant folding is only performed in functions – since the optimizer only runs in functions.
At that point though it also has a constant for the value on nt and so can just constant fold all the way to an answer. like @Benny said.
julia> function foo()
nt = (a=1, b=2, c=3)
t = (:b, :c)
nt[t]
end
foo (generic function with 1 method)
julia> @code_llvm foo()
; Function Signature: foo()
; @ REPL[1]:1 within `foo`
define void @julia_foo_1754([2 x i64]* noalias nocapture noundef nonnull sret([2 x i64]) align 8 dereferenceable(16) %sret_return) #0 {
top:
%0 = bitcast [2 x i64]* %sret_return to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %0, i8* noundef nonnull align 8 dereferenceable(16) bitcast ([2 x i64]* @"_j_const#1" to i8*), i64 16, i1 false)
ret void
}
julia> using BenchmarkTools
Precompiling BenchmarkTools
5 dependencies successfully precompiled in 11 seconds. 32 already precompiled.
julia> @btime foo()
0.855 ns (0 allocations: 0 bytes)
(b = 2, c = 3)
If we pass it in as an interpolated value we can get the right timing:
julia> function bar(nt)
t = (:b, :c)
nt[t]
end
bar (generic function with 1 method)
julia> @btime bar($(;a=1,b=2,c=3))
1.498 ns (0 allocations: 0 bytes)
(b = 2, c = 3)