Allocations in a copy! construct

Consider the following variations of in-place copying:

lower = 1.:3
upper = 12.:16

function a()
    points = zeros(100)
    allocs = @allocated copy!(points, [lower[1:end-1]; upper[1:end-1]])
    println("case a: $allocs bytes")
    resize!(points, length(lower)+length(upper)-2)
end

function b()
    points = zeros(100)
    allocs = @allocated begin       
            copy!(points, lower[1:end-1])
            copy!(points, length(lower), upper[1:end-1])
            end
    println("case b: $allocs bytes")
    resize!(points, length(lower)+length(upper)-2)    
end
        
function c()
    points = zeros(100)
    allocs = @allocated begin
            copy!(points, @view(lower[1:end-1]))
            copy!(points, length(lower), @view(upper[1:end-1]))
            end
    println("case c: $allocs bytes")
    resize!(points, length(lower)+length(upper)-2)
end

The result that i get (in the second run), is:

julia> a(); b(); c();
case a: 320 bytes
case b: 192 bytes
case c: 1728 bytes
  • In case a, does [lower[1:end-1]; upper[1:end-1]] as a second argument to copy! create a temporary vector, while in case b it doesn’t, explaining that they differ roughly in twice as many allocations?
  • For case c, why does it allocate that much, shouldn’t it be the same as case b?

This is the global scope problems. If you declare lower and upper as const, only a() allocates.

1 Like

Huh, i thought that enclosing the @allocated macros inside the function was enough. Thanks for the tip!!

Now cases b and c allocate exactly 0 bytes, now i see that’s the behavior to be expected there.

Yes, global variables are evil (hopefully they’ll get optimized a bit at some point). There’s a trick though : it only does not allocate because lower and upper are ranges. If they are arrays (with collect() in front of the ranges) then b and c allocate. b has to allocate because indexing is a copy, the fact that c allocates is a bug (I think it’s on the devs radar, but low priority)

1 Like

Ok, interesting. Then copy!(points, lower[1:end-1]) didn’t create temporaries just because lower is a lazy array, i overlooked that. Trying with arrays behaves as you say, case c allocating less than b (but not exactly zero).