Why is this simple function allocating?

I have a simple struct and a corresponding oneunit method. I find that the oneunit function allocates memory when I use it on my struct. How can I avoid this behavior?

using BenchmarkTools
import Base.oneunit

struct MyType{X<:AbstractFloat}
    x::X
    n::Int
end

oneunit(a::MyType) = MyType(oneunit(a.x), a.n)

a = MyType(1.2, 2)

@ballocated oneunit(a)

The @ballocated macro gives an output 32, which is twice sizeof(a).

I also ran @code_warntype, although I don’t know how to interpret the output.

julia> @code_warntype oneunit(a)
MethodInstance for oneunit(::MyType{Float64})
  from oneunit(a::MyType) @ Main REPL[50]:1
Arguments
  #self#::Core.Const(oneunit)
  a::MyType{Float64}
Body::MyType{Float64}
1 ─ %1 = Base.getproperty(a, :x)::Float64
│   %2 = Main.oneunit(%1)::Core.Const(1.0)
│   %3 = Base.getproperty(a, :n)::Int64
│   %4 = Main.MyType(%2, %3)::Core.PartialStruct(MyType{Float64}, Any[Core.Const(1.0), Int64])
└──      return %4
julia> @ballocated oneunit($a)
0

or see

julia> function asd()
           a = MyType(1.2, 2)
           oneunit(a)
       end
asd (generic function with 1 method)

julia> @ballocated asd()
0

I generally recommend to interpolate values into benchmarks with $.

2 Likes

Thank you!

What does the $a syntax do?

As I wrote, it interpolates the value into the benchmark (think hardcoding the value into the benchmark to avoid lookup costs etc.). See Manual · BenchmarkTools.jl for more information.

Thanks!