Too much garbage collection for a simple vector addition operation

Hi everyone,

I’m trying to optimize my code. When look at the profiler, the 2nd and the 3rd line below got GC flags:

function resourceAvailable(v::Int32, a::Int32, usedResource::Vector{Int32}, md::ModelData)
    neededResource::Vector{Int32} = usedResource .+ md.r[md.ct[v]][a]
    return all(x -> x >= 0, md.cap .- neededResource), neededResource
end

This is my ModelData struct:

struct ModelData
        ct::Vector{Int32}
        cap::Vector{Int32}
        r::Vector{Vector{Vector{Int32}}}
end

I tried some things in the StaticArrays package but nothing help. Can someone please explain why? Here is the profiler result:

image

Thanks.

if md.ct[v] is a vector, maybe use @views in front of this line could help

Hi,

If you mean:

@views neededResource::Vector{Int32} = usedResource .+ md.r[md.ct[v]][a]

I tried it, nothing change :frowning:

something must changed, because before you’re allocating md.r[md.ct[v]] now you’re not.

But otherwise yeah, if this is what you need to do, nothing can be optimized

Sorry I don’t understand. It’s not just accessing? Why does it need to allocate something?
And md.ct[v] is an integer, not a vector.

Both . (dot) operations allocate new arrays. The second one can be completely avoided, using,
for example*:

all(>=(0), md.cap[i] - neededResource[i] for i in eachindex(md.cap, neededResource))

(using both arrays in eachindex will guarantee that the operation is inbounds, but you could in put only one of them for simplicity)

The first one is allocating a new array neededResource, and the only way to avoid that is to preallocate it outside the function, to make the function in-place.

* there are alternatives, for example:

all(>=(0), x[1] - x[2] for x in zip(md.cap, neededResource))
4 Likes

Thank you!
I modified the function according to your suggest:

function resourceAvailable(usedResource::Vector{Int32}, neededResource::Vector{Int32}, actResource::Vector{Int32}, capResource::Vector{Int32})
    @inbounds for i in eachindex(neededResource, usedResource, actResource)
        neededResource[i] = usedResource[i] + actResource[i]
    end
    return all(>=(0), capResource[i] - neededResource[i] for i in eachindex(capResource, neededResource))
end

And All the GC flags gone!

1 Like

This loop is totally fine. But you can write it as:

neededResource .= usedResource .+ actResource

or

@. neededResource = usedResource + actResource

The broadcasts won’t allocate now that you are updating an existing array.

1 Like

Another alternative for the second broadcasting line:

all(>=(0), Iterators.map(-, md.cap, neededResource))
1 Like

Why not combine the predicate and subtraction? The optimizer can probably resolve this, but it seems like messy code to write. But really, why subtract and compare to 0 when you can compare the numbers directly?

all(splat(>=), zip(md.cap, neededResource))
3 Likes

Hmm you are right that sound logic I’ll do that! Thanks.

Or, arguably a bit more readable:

all(md.cap[i] >= neededResource[i] for i in eachindex(md.cap))
2 Likes

Or

all(Iterators.map(>=, md.cap, neededResource))
2 Likes