# Why making \`a\[range\] = b\[range\]\` seems to make unnecessary allocations?

**URL:** <https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325>\
**Category:** Performance\
**Created:** [September 30, 2019, 2:20pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325 "2019-09-30T14:20:20Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![pedrohnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pedrohnv/32/212503_2.png) [@pedrohnv](https://discourse.julialang.org/u/pedrohnv)\
**Post date:** [September 30, 2019, 2:20pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325/1 "2019-09-30T14:20:20Z")

</div>

I was wondering why making `a[range] = b[range]` seems to make unnecessary allocations and if (why) that is intended. For example:

```julia
function f!(a, b, n)
    a[1:n, 1:n] = b[1:n, 1:n]
end

function g!(a, b, n)
    copyto!(a, CartesianIndices((1:n, 1:n)), b, CartesianIndices((1:n, 1:n)))
end

function h!(a, b, n)
    for i = 1:n 
        for k = 1:n 
            a[k, i] = b[k, i]
        end
    end
end
n = 500
a = zeros(n,n)
b = rand(n,n)

@time f!(a,b,n); # 0.002301 seconds (6 allocations: 1.908 MiB)
@time g!(a,b,n); # 0.000327 seconds (4 allocations: 160 bytes)
@time h!(a,b,n); # 0.000492 seconds (4 allocations: 160 bytes)

```

Doing the for loops (`h!`) seems to be the optimal way allocation-wise if the idea is to make a copy from `transpose(b)` (inside the function) or any kind of column or line permutation.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [September 30, 2019, 2:24pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325/2 "2019-09-30T14:24:03Z")

</div>

`a[range]` makes a copy for a mix of historical, and memory locality reasons. the simplest solution is to use

```julia
function g!(a, b, n)
    a[1:n, 1:n] = @view b[1:n, 1:n]
end

```

which will remove the copy.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [September 30, 2019, 2:57pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325/3 "2019-09-30T14:57:14Z")

</div>

> [@pedrohnv](#):
>
> b[1:n, 1:n]

Slicing an array in Julia creates a copy, which is where your allocations are coming from. You can do `@view(b[1:n, 1:n])` to create a view instead, which will avoid the unnecessary copy. See [Could you explain what are views? - #3 by stevengj](https://discourse.julialang.org/t/could-you-explain-what-are-views/17535/3) for some more explanation.

---

<div class="post-metadata">

**Author:** ![pedrohnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pedrohnv/32/212503_2.png) [@pedrohnv](https://discourse.julialang.org/u/pedrohnv)\
**Post date:** [September 30, 2019, 9:07pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325/5 "2019-09-30T21:07:36Z")

</div>

That does not work when copying an array into itself.

Setup:

```julia
n = 1000;
m = fld(n,2);
a = zeros(n,n);
a[1:m, 1:m] .= randn.();
v = view(a, m:n, m:n);

```

```julia
function f!(a, b, n)
    @views a[1:n, 1:n] = b[1:n, 1:n]
end
@btime f!(v, a, m); # 1.490 ms (7 allocations: 1.91 MiB)
b = rand(n,n);
@btime f!(v, b, m); # 931.648 μs (1 allocation: 64 bytes)

```

Using `copyto!` leads to the same

```julia
function f!(a, b, n)
    copyto!(a, CartesianIndices((1:n, 1:n)), b, CartesianIndices((1:n, 1:n)))
end
@btime f!(v, a, m); # 997.360 μs (2 allocations: 7.63 MiB)
@btime f!(v, b, m); # 238.248 μs (0 allocations: 0 bytes)

```

Best approach is to use explicit for loops

```julia
function f!(a, b, n)
    for k = 1:n
        for i = 1:n
            a[i,k] = b[i,k]
        end
    end
end
@btime f!(v, a, m); # 312.512 μs (0 allocations: 0 bytes)
@btime f!(v, b, m); # 312.134 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [September 30, 2019, 10:18pm UTC](https://discourse.julialang.org/t/why-making-a-range-b-range-seems-to-make-unnecessary-allocations/29325/7 "2019-09-30T22:18:33Z")

</div>

Note that if you benchmark the functions properly, the looping function is slower for the matrix sizes you’re looking at (For _very_ small matrices, the looping version will still win)

```julia
function g!(a, b, n)
    copyto!(a, CartesianIndices((1:n, 1:n)), b, CartesianIndices((1:n, 1:n)))
end

function h!(a, b, n)
    for i = 1:n 
        for k = 1:n 
            a[k, i] = b[k, i]
        end
    end
end
n = 500;
a = zeros(n,n);
b = rand(n,n);

@btime g!(a,$b,$n) setup=(a=zeros(n, n)); # 110.865 μs (0 allocations: 0 bytes)
@btime h!(a,$b,$n) setup=(a=zeros(n, n)); # 162.373 μs (0 allocations: 0 bytes)

```

`f!` can be sped up a bit by using UnsafeViews.jl but it seems to still be slower than the `copyto!` solution.
