The culprit was relying on Base.@locals to fetch variable names and values which is slow and won’t be optimized away by the compiler. What I realized recently was that closures actually store the names and values of the bound variables in themselves, so there was no need for Base.@locals.
Here’s a minimal working example of the problem:
julia> f(x) = (Base.@locals(), y -> x + y)
f (generic function with 1 method)
julia> let x = Ref(1), y = Ref(2)
@btime f($x[])[2]($y[])
end
103.571 ns (4 allocations: 608 bytes)
3