# Why is there an allocation?

``````module mbas001
elxs = fill(Matrix{Float64}(undef, 3, 3), 7)
for _i in eachindex(elxs)
elxs[_i] = rand(3, 3)
end
ex = fill(zero(Float64), 3, 3)
copy2ex!(ex, elxs, j, k) = let
elxsc = elxs[c]
for _i in axes(ex, 2)
ex[1, _i] = elxsc[j, _i];
ex[2, _i] = elxsc[k, _i];
end
ex
end
c = 4
@time let
ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
end
@time copy2ex!(ex, elxs, 1, 2)
@time copy2ex!(ex, elxs, 1, 2)
@time copy2ex!(ex, elxs, 1, 2)
end
``````

produces

``````julia> include("C:\\Users\\pkonl\\Documents\\00WIP\\HelmBEM.jl\\test\\littletest.jl")
WARNING: replacing module mbas001.
0.000023 seconds (6 allocations: 224 bytes)
0.018632 seconds (9.56 k allocations: 517.691 KiB, 99.73% compilation time)
0.000017 seconds (6 allocations: 96 bytes)
0.000016 seconds (6 allocations: 96 bytes)
Main.mbas001
``````

What puzzles me is why the function `copy2ex!` allocates memory.
It happens in the loop

``````    for _i in axes(ex, 2)
ex[1, _i] = elxsc[j, _i];
ex[2, _i] = elxsc[k, _i];
end
``````

Which, in my understanding, should be simply assignment of one matrix location the value of another matrix location. In other words, scalars.

`c` is a non-const global variable.

3 Likes

Wow. I missed that! Thanks.

Now if I could understand why the memory allocation happens here:

``````ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
``````

There are still 6 ALLOCATIONS:

``````WARNING: replacing module mbas001.
0.000025 seconds (6 allocations: 224 bytes)
0.022149 seconds (6.53 k allocations: 337.683 KiB, 99.85% compilation time)
0.000015 seconds
0.000003 seconds
Main.mbas001
``````

The code looks like this now:

``````module mbas001
elxs = fill(Matrix{Float64}(undef, 3, 3), 7)
for _i in eachindex(elxs)
elxs[_i] = rand(3, 3)
end
ex = fill(zero(Float64), 3, 3)
copy2ex!(ex, elxs, c, j, k) = let
elxsc = elxs[c]
for _i in axes(ex, 2)
ex[1, _i] = elxsc[j, _i];
ex[2, _i] = elxsc[k, _i];
end
ex
end
@time let
c = 4
ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
end
c = 4
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
end
``````

const

const

``````@time @views let
``````

Now everything is a local variable. Still, there are allocations.

``````module mbas001
let
elxs = fill(Matrix{Float64}(undef, 3, 3), 7)
for _i in eachindex(elxs)
elxs[_i] = rand(3, 3)
end
ex = fill(zero(Float64), 3, 3)
copy2ex!(ex, elxs, c, j, k) = let
elxsc = elxs[c]
for _i in axes(ex, 2)
ex[1, _i] = elxsc[j, _i];
ex[2, _i] = elxsc[k, _i];
end
ex
end
@time @views let
c = 4
ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
end
c = 4
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
end
end
``````

gives

``````WARNING: replacing module mbas001.
0.000013 seconds (15 allocations: 512 bytes)
0.020667 seconds (6.54 k allocations: 338.026 KiB, 99.88% compilation time)
0.000005 seconds (9 allocations: 352 bytes)
0.000003 seconds (9 allocations: 352 bytes)
Main.mbas001
``````

Edit: Note that I forgot to remove the `views`. Everything is local now.

Doesn’t `@time` itself allocate? I always use `@btime` and variable interpolation for reliable allocation estimates.

It actually worked (sort of) in https://discourse.julialang.org/t/why-is-there-an-allocation/85405/4: there were zero allocations with the function.

`@btime` seems to have improved over time, but it might still be an idea to use BenchmarkTools to be more certain.

please implement all the changes I proposed:

``````julia> module mbas001
const elxs = fill(Matrix{Float64}(undef, 3, 3), 7)
for _i in eachindex(elxs)
elxs[_i] = rand(3, 3)
end
const ex = fill(zero(Float64), 3, 3)
copy2ex!(ex, elxs, c, j, k) = let
elxsc = elxs[c]
for _i in axes(ex, 2)
ex[1, _i] = elxsc[j, _i];
ex[2, _i] = elxsc[k, _i];
end
ex
end
@time @views let
c = 4
ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
end
c = 4
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
@time copy2ex!(ex, elxs, c, 1, 2)
end
0.000001 seconds
0.012696 seconds (7.42 k allocations: 388.971 KiB, 99.93% compilation time)
0.000003 seconds
0.000002 seconds
Main.mbas001
``````

Thank you, I appreciate your suggestion, but really I want to have only local variables.
Testing in the global scope was obviously a mistake.

It is possible that `@time` allocates when it is used in a local scope. Tracking allocations
with everything in local scope:

``````        - module mbas001
- let
- elxs = fill(Matrix{Float64}(undef, 3, 3), 7)
- for _i in eachindex(elxs)
-     elxs[_i] = rand(3, 3)
- end
- ex = fill(zero(Float64), 3, 3)
- copy2ex!(ex, elxs, c, j, k) = let
0     elxsc = elxs[c]
0     for _i in axes(ex, 2)
0         ex[1, _i] = elxsc[j, _i];
0         ex[2, _i] = elxsc[k, _i];
0     end
0     ex
- end
- let
-     c = 4
-     ex[1,:] = elxs[c][1,:]; ex[2,:] = elxs[c][2,:];
- end
- c = 4
- copy2ex!(ex, elxs, c, 1, 2)
- copy2ex!(ex, elxs, c, 1, 2)
- copy2ex!(ex, elxs, c, 1, 2)
- end
- end
``````