Hello!
I want to create a structure that stores (besides other data) some functions. It is convinient to store them together in a one structure.
But consider the following code (MWE):
using BenchmarkTools
struct Foo
f::Function
end
function calculate(sys::Foo, x::Vector{Float64})::Nothing
for el in x
sys.f(el)
end
end
function main()
f(x::Float64)::Float64 = sin(x)
sys = Foo(f)
N = 100_000
x = zeros(N)
@time (
for el in x
sys.f(el)
end
)
calculate(sys, x) # compilation
@time calculate(sys, x)
@code_warntype calculate(sys, x)
end
main()
Notice that when I pass sys
into calculate
function and then use sys.f, it allocates too much.
@code_warntype
says:
MethodInstance for calculate(::Foo, ::Vector{Float64})
from calculate(sys::Foo, x::Vector{Float64}) in Main at C:\Users\densh\Documents\Science\test.jl:6
Arguments
#self#::Core.Const(calculate)
sys::Foo
x::Vector{Float64}
Locals
@_4::Union{Nothing, Tuple{Float64, Int64}}
el::Float64
Body::Nothing
1 ─ %1 = Main.Nothing::Core.Const(Nothing)
│ %2 = x::Vector{Float64}
│ (@_4 = Base.iterate(%2))
│ %4 = (@_4 === nothing)::Bool
│ %5 = Base.not_int(%4)::Bool
└── goto #4 if not %5
2 ┄ %7 = @_4::Tuple{Float64, Int64}
│ (el = Core.getfield(%7, 1))
│ %9 = Core.getfield(%7, 2)::Int64
│ %10 = Base.getproperty(sys, :f)::Function
│ (%10)(el)
│ (@_4 = Base.iterate(%2, %9))
│ %13 = (@_4 === nothing)::Bool
│ %14 = Base.not_int(%13)::Bool
└── goto #4 if not %14
3 ─ goto #2
4 ┄ %17 = Base.convert(%1, nothing)::Core.Const(nothing)
│ %18 = Core.typeassert(%17, %1)::Core.Const(nothing)
└── return %18
where ::Function
is highlighted with red color, which means that Function
isn’t a non-concrete type (but how is it connected with allocatoins I still don’t understand).
So, my question is what am I do wrong and what is the proper way of “storing functions in a structures” in a sense of performance.
Thanks!