Ok, I see that my function h
(revised to take an arguement as h1
)
does indeed not allocated if it is working on bitstypes.
function h1(tup)
return (getfield(tup, 1), getfield(tup, 3))
end;
julia> @btime h1((10,20,30))
0.029 ns (0 allocations: 0 bytes)
(10, 30)
julia> @btime h1((:a, :b, :c))
6.717 ns (1 allocation: 32 bytes)
(:a, :c)
If we are willing to be convoluted, this can be made nonallocating by putting everything into type params.
Putting things into typeparams just to make them isbits feels odd,
and I think is only doable with Symbols
?
function h_t(tup)
return Val{(getfield(tup, 1), getfield(tup, 3))}
end;
h_i(::Type{Val{L}}) where L = L
h2(tup) = h_i(h_t(tup))
julia> @btime h2((10,20,30))
0.029 ns (0 allocations: 0 bytes)
(10, 30)
julia> @btime h2((:a, :b, :c))
1.246 ns (0 allocations: 0 bytes)
(:a, :c)
In the microbench mark it works out to be faster.
But I am dubious as to if it will in practice.
The @code_typed
for h2
is just a worse version of that for h1
)
julia> @code_typed h1((:a, :b, :c))
CodeInfo(
1 ─ %1 = (Main.getfield)(tup, 1)::Symbol
│ %2 = (Main.getfield)(tup, 3)::Symbol
│ %3 = (Core.tuple)(%1, %2)::Tuple{Symbol,Symbol}
└── return %3
) => Tuple{Symbol,Symbol}
julia> @code_typed h2((:a, :b, :c))
CodeInfo(
1 ─ %1 = (Main.getfield)(tup, 1)::Symbol
│ %2 = (Main.getfield)(tup, 3)::Symbol
│ %3 = (Core.tuple)(%1, %2)::Tuple{Symbol,Symbol}
│ %4 = (Core.apply_type)(Main.Val, %3)::Type{Val{_1}} where _1
│ %5 = (Main.h_i)(%4)::Any
└── return %5
) => Any
but it seems inpractice that h2
is actually better.
julia> @btime (()->hash(h1((:a, :b, :c))))()
13.260 ns (1 allocation: 32 bytes)
0x3098f6b4cf50f6cc
julia> @btime (()->hash(h2((:a, :b, :c))))()
8.031 ns (0 allocations: 0 bytes)
0x3098f6b4cf50f6cc