I’m trying to reduce latency in a package, and was intrigued by the mention of “type hiding” in the Dataframes.jl Juliacon talk by @bkamins (State of DataFrames.jl - YouTube).
As I understand it, type hiding consists of wrapping a variable that can have a wide range of types inside a struct without any free parameters, so that its type is only dealt with deep inside a function call tree, when it is unwrapped. This avoids unnecessary specialization and inference along the way.
Problem: I cannot make this kind of approach allocation-free. Here is an absolutely minimal example:
julia> struct S
f
end
julia> test(s::S, i) = (s.f() < i) === true;
julia> x = S(randn);
julia> using BenchmarkTools; @btime test($x, 0);
32.438 ns (1 allocation: 16 bytes)
Note that, despite f
being of type Any
, test
always returns a Bool
, so any non-inferable types are confined within test
, which unwraps the problematic f::Any
field. The goal is to make test
allocation-free, in spite of this. That single allocation might seem irrelevant, but it isn’t if you put this test
in a hot loop.
If I use the standard approach of fully parametrized types (i.e. no “type-hiding”), I can get rid of the allocation, of course
julia> struct S{F}
f::F
end
julia> test(s::S, i) = (s.f() < i) === true;
julia> x = S(randn);
julia> using BenchmarkTools; @btime test($x, 0);
5.611 ns (0 allocations: 0 bytes)
Question: is there any way to make this kind of type-hiding trick zero-cost in terms of allocations and runtime?
Constraints: I’d like to avoid Base.@pure
if possible, as I can make no assumption on the field f
. Also, the field f
might be of any type, not just Function
Thanks for any help!