# Memory allocation with \`view\` and array assignment

**URL:** <https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109>\
**Category:** General Usage\
**Tags:** array, memory-allocation\
**Created:** [August 27, 2021, 3:54pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109 "2021-08-27T15:54:53Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [August 27, 2021, 3:54pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/1 "2021-08-27T15:54:53Z")

</div>

Hey,

I was wondering about this code:

```julia
julia> x = randn((2048, 2048));

julia> z = copy(x);

julia> using BenchmarkTools

julia> @btime $z[2:2000, 2:2000] = view($x, 2:2000, 2:2000);
  5.386 ms (0 allocations: 0 bytes)

julia> @btime $z[2:2000, 2:2000] .= view($x, 2:2000, 2:2000);
  3.195 ms (0 allocations: 0 bytes)

julia> @btime $z[2:2000, 2:2000] = view($z, 2:2000, 2:2000); # why allocations?
  9.731 ms (2 allocations: 30.49 MiB)

julia> @btime $z[2:2000, 2:2000] .= view($z, 2:2000, 2:2000); # why allocations?
  6.859 ms (2 allocations: 30.49 MiB)

```

I would have expected zero allocations for all of the examples, but why are there allocations if I assign the array to itself? Is it due to the reason that without a copy, the following

```julia
julia> @btime $z[2:2000, 2:2000] .= view($z, 1:1999, 1:1999);
  6.871 ms (2 allocations: 30.49 MiB)

julia> @btime $z[1:1999, 1:1999] .= view($z, 2:2000, 2:2000);
  6.872 ms (2 allocations: 30.49 MiB)

```

could fail because of overwriting?

Thanks for help!

Felix

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [August 27, 2021, 4:01pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/2 "2021-08-27T16:01:39Z")

</div>

It’s due to the risk of [aliasing](https://en.wikipedia.org/wiki/Aliasing_(computing)). See `?Base.unalias` for more details.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [August 27, 2021, 4:11pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/3 "2021-08-27T16:11:40Z")

</div>

This actually looks like a bug. We have the smarts to see that the indices are matched between the LHS and the RHS (and thus is memory safe). I’m not sure why this simple case isn’t getting the fast-path, but a more complicated expression avoids the conservative copy:

```nohighlight
julia> @btime $z[2:2000,2:2000] .= view($z, 2:2000, 2:2000);
  12.624 ms (2 allocations: 30.49 MiB)

julia> @btime $z[2:2000,2:2000] .= 0 .+ view($z, 2:2000, 2:2000);
  2.654 ms (0 allocations: 0 bytes)

julia> @btime $z[2:2000,2:2000] .= 0 .+ view($z, 1:1999, 1:1999);
  13.290 ms (2 allocations: 30.49 MiB)

```

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [August 27, 2021, 4:47pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/4 "2021-08-27T16:47:31Z")

</div>

If there’s a check that notices the source and destination arrays are equal, but so are the indices, couldn’t we make a fastest-path (no-op)?

Although custom array types would have the problem of the possiblity of specialized `getindex` and `setindex!` methods.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 27, 2021, 4:51pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/5 "2021-08-27T16:51:48Z")

</div>

if an object is such that

`obj[a] = obj[a]` is not conceptually no-op… I think it’s doomed either way

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [August 27, 2021, 4:54pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/6 "2021-08-27T16:54:33Z")

</div>

I mean sure, there’s no limit to how far down you can go on the optimizations… but every one you make has a cost in the surface area that generic behaviors need to accommodate — and I’d say this is of fairly low utility. I’m guessing it was an MWE reduced down from something like `x .= f.(x)` (which is similarly missing the aliasing fast-path, perhaps because it has another “fast-path” it’s using).

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [August 27, 2021, 5:03pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/7 "2021-08-27T17:03:24Z")

</div>

Maybe some hacky use of [MappedArrays](https://github.com/JuliaArrays/MappedArrays.jl) where the inverse is only an approximation / not exact?  
Maybe someone could implement an algorithm that iteratively converges.

(Obviously `log(exp(x))` isn’t exact either, but I think people wouldn’t mind that turning into a no-op.)

> [@mbauman](#):
>
> I mean sure, there’s no limit to how far down you can go on the optimizations… but every one you make has a cost in the surface area that generic behaviors need to accommodate — and I’d say this is of fairly low utility.

I agree. The more checks like this that exist that can’t be skipped, the more likely I am to re-implement things when micro-optimizing better reflecting the problem at hand.

Here the costs would be in (a) the check itself and (b) forwarding the result of just returning to the function calling the check. So if one does add an extra check for if indices match, then it seems like additionally adding a “fastest-path” hopefully shouldn’t require much more code (unless forwarding the results is especially costly).

I think in general, no one should be doing `a .= a` if it doesn’t do anything.  
But `a .= b` is common, and someone may have code that often uses internal caches, and is called both internally by their library, as well as by users. Then because in that particular case they might think exact aliasing is likely, it’d be easy enough to optimize manually with `a === b || (a .= b)`.

---

<div class="post-metadata">

**Author:** ![anon56330260](https://avatars.discourse-cdn.com/v4/letter/a/f07891/32.png) [@anon56330260](https://discourse.julialang.org/u/anon56330260)\
**Post date:** [August 28, 2021, 4:41am UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/8 "2021-08-28T04:41:20Z")

</div>

Maybe we could perform a runtime memory dependency test. It’s much more general than just handling this trivial case. It can also handle partially overlapping case and avoid copying. `memmove` does exactly this, though it only deals with one dimensional array…

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [November 10, 2021, 1:57am UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/9 "2021-11-10T01:57:15Z")

</div>

Now I encountered a real case where I didn’t expect allocation.

Isn’t that something we should fix?

```julia
julia> function f(x,y)
           x .= y.^2 .+ x.^2
       end
f (generic function with 1 method)

julia> function g(x,y)
           x .= view(y, 1:length(y)).^2 .+ view(x, 1:length(x)).^2
       end
g (generic function with 1 method)

julia> x, y = rand(Int, 2048*2048), rand(Int, 2048*2048);

julia> @time f(x,y);
  0.030055 seconds (15.08 k allocations: 717.336 KiB, 83.78% compilation time)

julia> @time f(x,y);
  0.005222 seconds

julia> @time g(x,y);
  0.076428 seconds (233.42 k allocations: 44.631 MiB, 77.89% compilation time)

julia> @time g(x,y);
  0.026629 seconds (2 allocations: 32.000 MiB)

```

> **Performance seems to be significantly different as well:**
>
> ```julia
> julia> x, y = rand(Int, 5000*10_000), rand(Int, 5000*10_000);
> 
> julia> @btime f($x, $y);
> 53.750 ms (0 allocations: 0 bytes)
> 
> julia> @btime g($x, $y);
> 209.211 ms (2 allocations: 381.47 MiB)
> 
> julia> x, y = rand(Int, 10*10), rand(Int, 10*10);
> 
> julia> @btime f($x, $y);
> 49.186 ns (0 allocations: 0 bytes)
> 
> julia> @btime g($x, $y);
> 114.352 ns (1 allocation: 896 bytes)
> 
> ```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [November 10, 2021, 2:39am UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/10 "2021-11-10T02:39:46Z")

</div>

That looks like an anti-aliasing false positive.

---

<div class="post-metadata">

**Author:** ![N5N3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/n5n3/32/17663_2.png) [@N5N3](https://discourse.julialang.org/u/N5N3)\
**Post date:** [December 17, 2021, 6:59am UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/11 "2021-12-17T06:59:23Z")

</div>

Well, `@btime $z[2:2000, 2:2000] .= view($z, 2:2000, 2:2000);` fallback to `copyto!(a::AbstractArray, b::AbstractArray)`, where we always do `unalias` and performs no `a === b` check.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [December 17, 2021, 1:03pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/12 "2021-12-17T13:03:34Z")

</div>

> [@roflmaostc](#):
>
> ```julia
> julia> function f(x,y)
> x .= y.^2 .+ x.^2
> end
> f (generic function with 1 method)
> 
> julia> function g(x,y)
> x .= view(y, 1:length(y)).^2 .+ view(x, 1:length(x)).^2
> end
> g (generic function with 1 method)
> 
> julia> x, y = rand(Int, 2048*2048), rand(Int, 2048*2048);
> 
> julia> @time f(x,y);
> 0.030055 seconds (15.08 k allocations: 717.336 KiB, 83.78% compilation time)
> 
> julia> @time f(x,y);
> 0.005222 seconds
> 
> julia> @time g(x,y);
> 0.076428 seconds (233.42 k allocations: 44.631 MiB, 77.89% compilation time)
> 
> julia> @time g(x,y);
> 0.026629 seconds (2 allocations: 32.000 MiB)
> 
> ```

The `copyto!` doesn’t fix that, does it?

---

<div class="post-metadata">

**Author:** ![N5N3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/n5n3/32/17663_2.png) [@N5N3](https://discourse.julialang.org/u/N5N3)\
**Post date:** [December 17, 2021, 1:19pm UTC](https://discourse.julialang.org/t/memory-allocation-with-view-and-array-assignment/67109/13 "2021-12-17T13:19:17Z")

</div>

This example seems unrelated with `copyto!(A::AbstractArray, B::AbstractArray)`, as it fallback to `copyto!(::AbstractArray, ::Broacasted{Nothing})`. Since we only use `a === b` to avoid copy during self-inplace broadcast, you’d better avoid this pattern (‘g’) during usage.
