Help eliminating allocations

Hi!

I need to reproduce a code in a Kernel include file. In a part, it computes the size of an array of chars. Initially, I was doing something like this, which perfectly mimics the behavior in the .h:

function test(a::AbstractVector)
       m = length(a)
       return sizeof(NTuple{m, Cchar})
end

Note: This is just a MWE of what I am seeing.

The problem is that it allocates three times:

julia> data = [1,2,3,4]
4-element Array{Int64,1}:
 1
 2
 3
 4

julia> @btime test($data)
  356.243 ns (3 allocations: 192 bytes)
4

The allocation is coming from the type NTuple{m, Cchar} that depends on m. Of course I can avoid it by just replacing with m*sizeof(Cchar). However, this leads to many changes in a lot of functions. Thus, is there a way to create a type so that sizeof returns what I want without allocations?

I’m not sure I understand, wouldn’t any other solution also require those changes?

Not sure I understand, but would this work for you?

struct Foo{T}
    nelems :: Int
end

Foo(nelems, T) = Foo{T}(nelems)
Base.sizeof(x :: Foo{T}) where {T} = x.nelems * sizeof(T)


function test(a::AbstractVector)
    m = length(a)
    return sizeof(Foo(m, Cchar))
end
julia> using BenchmarkTools
julia> data = [1,2,3,4];
julia> @btime test($data)
  1.879 ns (0 allocations: 0 bytes)
4

I mean, just do the changes then? This feels a bit like saying that the car runs slowly because there’s a puncture on all tires and asking if there is any way to make it faster that doesn’t involve changing the tires.

3 Likes

Creating a type using runtime information is kind of the definition of “not type-stable”.

However, if the C code that you are translating is passing a fixed-length data vector then you could translate that into a Tuple or an SVector or MVector (from StaticArrays) instead of a Vector. That will make m a compile-time constant, and get rid of the allocations.

I mean, if I change this, then I will loose the 1 to 1 relationship with the kernel functions. Thus, maybe in the future, if the kernel API changes, then it will be a little harder to update this file. Of course this is not the end of the world, I am just curious how can I avoid that problem :slight_smile:

Nice, the idea to overload sizeof just for that case was very good! Thanks!

Hum, I see. Unfortunately I can’t. Because this vector contains the message the user wants to transmit using SPI interface. It will be very bad to restrict to only the type in StaticArrays.jl. What I can do, it to force the user to use Tuples instead. I will see, but the proposal of @ffevotte seems very good.

Note that it’s not the overloading of sizeof that matters. It’s the fact that Foo doesn’t get m as a type parameter. Essentially, this is just a very convoluted way of writing m*sizeof(Cchar).

3 Likes