I’ve simplified my unnecessarily complicated post to show a simple case where FunctionWrappers allocate whereas using a closure without function argument types doesn’t allocate. I’m not sure why this happens. Any help on this topic is appreciated.
# %%
using StaticArrays
using BenchmarkTools
import FunctionWrappers
# %%
struct DummyModel{I}
invoker::FunctionWrappers.FunctionWrapper{Float32,Tuple{MVector{I,Float32}}}
modulecode
end
function compilemodel(y::Vector{Float32})
I = length(y)
runbody = quote
function run(x::MVector{$I,Float32})
m = SVector{$I,Float32}($y)
first(x-m)
end
end
runinvokerbody = quote
runinvoker() = run
end
modulecode = Expr(:block, :(using StaticArrays), runbody, runinvokerbody)
moduleexpr = Expr(:module, true, gensym(:CompiledModel), modulecode)
eventmodule = eval(moduleexpr)
DummyModel{I}(Base.invokelatest(eventmodule.runinvoker), modulecode)
end
# %%
MT = MVector{3,Float32}
modelvec = Vector{Float32}([1,2,3])
model = compilemodel(modelvec)
x = MT([1,2,3])
@btime $(model).invoker($x)
# %%
struct DummyModel1
invoker::Function
end
function compilemodel1(y::Vector{Float32})
I = length(y)
func = function (x::MVector{I,Float32}) where {I}
m = SVector{I,Float32}(y)
first(x-m)
end
DummyModel1(func)
end
model1 = compilemodel1(modelvec)
@btime $(model1.invoker)($x)
Original Post:
I am trying to unroll some matrix operations using StaticArrays based on some model parameters without allocations. Following is a simplified example of how I was going about it in Julia 0.6. However, in Julia 1.1 the same code allocates.
What changes in Julia 1.x are causing this behavior and what is the recommended way to resolve this issue.
using StaticArrays
using BenchmarkTools
import FunctionWrappers
struct DummyModel{I}
invoker::FunctionWrappers.FunctionWrapper{Float32,Tuple{MVector{I,Float32}}}
modulecode
end
function compilemodel(y::Vector{Float32})
I = length(y)
runbody = quote
function run(x::MVector{$I,Float32})
m = SVector{$I,Float32}($y)
first(x-m)
end
end
runinvokerbody = quote
runinvoker() = run
end
modulecode = Expr(:block, :(using StaticArrays), runbody, runinvokerbody)
moduleexpr = Expr(:module, true, gensym(:CompiledModel), modulecode)
eventmodule = eval(moduleexpr)
DummyModel{I}(Base.invokelatest(eventmodule.runinvoker), modulecode)
end
function measure_allocations(model, input)
@allocated model.invoker(input)
end
# %%
MT = MVector{3,Float32}
model = compilemodel(Vector{Float32}([1,2,3]))
x = MT([1,2,3])
# measure_allocations(model, x)
@btime $(model).invoker($x) # 20.849 ns (1 allocation: 16 bytes)