# Counter-intuitive performance difference

**URL:** <https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477>\
**Category:** Performance\
**Tags:** question\
**Created:** [March 29, 2025, 1:54am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477 "2025-03-29T01:54:10Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![photor](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/photor/32/14343_2.png) [@photor](https://discourse.julialang.org/u/photor)\
**Post date:** [March 29, 2025, 1:54am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/1 "2025-03-29T01:54:10Z")

</div>

```julia
julia> using BenchmarkTools

julia> for i=1000
           A=randn(2i,2i)
           vA=@view A[1:i,1:i]
           C=randn(i,i)
           @btime @. $C+=$vA
       end
  1.325 ms (0 allocations: 0 bytes)

julia> for i=1000
           A=randn(2i,2i)
           vA=@view A[1:i,1:i]
           C=randn(i,i)
           @btime @. $C=$vA
       end
  2.490 ms (0 allocations: 0 bytes)

julia>

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 29, 2025, 3:05am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/2 "2025-03-29T03:05:57Z")

</div>

Corroborating this, just in one line:

```julia
julia> let i=1000; @btime $(randn(i,i)) .= $(view(randn(2i,2i), 1:i, 1:i)) end;
  983.500 μs (0 allocations: 0 bytes)

julia> let i=1000; @btime $(randn(i,i)) .+= $(view(randn(2i,2i), 1:i, 1:i)) end;
  687.600 μs (0 allocations: 0 bytes)

```

Difference intuitively reverses without the noncontiguous view, maybe that’s a hint:

```julia
julia> let i=1000; @btime $(randn(i,i)) .= $(randn(i,i)) end;
  270.300 μs (0 allocations: 0 bytes)

julia> let i=1000; @btime $(randn(i,i)) .+= $(randn(i,i)) end;
  627.700 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 30, 2025, 1:12am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/3 "2025-03-30T01:12:41Z")

</div>

> [@photor](#):
>
> `@btime @. $C=$vA`

Note that `A .= B` [dispatches to a specialized method](https://github.com/JuliaLang/julia/blob/470ff4860a88e680c0ac3684cacc01d91425178e/base/broadcast.jl#L949-L960) that is supposed to be a performance optimization, but maybe it is slower for a non-contiguous view.

---

<div class="post-metadata">

**Author:** ![photor](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/photor/32/14343_2.png) [@photor](https://discourse.julialang.org/u/photor)\
**Post date:** [March 30, 2025, 1:49am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/4 "2025-03-30T01:49:43Z")

</div>

Yes, exactly. But the default `copyto!(A,B)` is as slow as `A.=B`.  
The only thing I can do in this case is to use hand-writing for loops.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2025, 2:29am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/5 "2025-03-30T02:29:31Z")

</div>

I think that `identity`-dispatch is the difference, here’s what happens when I made `identity`-like functions (`@nospecialize` evidently makes no difference):

```julia
julia> id2(@nospecialize x)=x
id2 (generic function with 1 method)

julia> ids(x)=x
ids (generic function with 1 method)

julia> let i=1000
         @btime $(randn(i,i)) .= $(view(randn(2i,2i), 1:i, 1:i))
         @btime $(randn(i,i)) .= id2.($(view(randn(2i,2i), 1:i, 1:i)))
         @btime $(randn(i,i)) .= ids.($(view(randn(2i,2i), 1:i, 1:i)))
         @btime copyto!($(randn(i,i)), $(view(randn(2i,2i), 1:i, 1:i)))
         @btime $(randn(i,i)) .= $(randn(i,i))
         @btime $(randn(i,i)) .= id2.($(randn(i,i)))
         @btime $(randn(i,i)) .= ids.($(randn(i,i)))
         @btime copyto!($(randn(i,i)), $(randn(i,i)))
       end;
  982.700 μs (0 allocations: 0 bytes)
  692.600 μs (0 allocations: 0 bytes)
  687.600 μs (0 allocations: 0 bytes)
  989.000 μs (0 allocations: 0 bytes)
  297.000 μs (0 allocations: 0 bytes)
  622.700 μs (0 allocations: 0 bytes)
  617.500 μs (0 allocations: 0 bytes)
  264.900 μs (0 allocations: 0 bytes)

```

So the `identity`-dispatch [to `copyto!`](https://github.com/JuliaLang/julia/blob/470ff4860a88e680c0ac3684cacc01d91425178e/base/broadcast.jl#L984) works a treat on contiguous matrices with the same indices, not so much on views. Not sure why `.+=` adds almost no time on top of `.= id2.`, maybe it’s just that much cheaper than everything else.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 30, 2025, 11:13am UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/6 "2025-03-30T11:13:24Z")

</div>

> [@photor](#):
>
> The only thing I can do in this case is to use hand-writing for loops.

[This is the loop](https://github.com/JuliaLang/julia/blob/4d2a350870b97059432ac8fa8de92b0b161c335b/base/abstractarray.jl#L1092-L1098) it should be dispatching to. Not sure why this is slow?

---

<div class="post-metadata">

**Author:** ![photor](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/photor/32/14343_2.png) [@photor](https://discourse.julialang.org/u/photor)\
**Post date:** [March 30, 2025, 2:40pm UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/7 "2025-03-30T14:40:20Z")

</div>

I don’t know. But see the result:

```julia
julia> using BenchmarkTools

julia> cp!(C,A) = @inbounds @simd for i in CartesianIndices(C)
           C[i] = A[i]
       end
cp! (generic function with 1 method)

julia> for i=1000
           A=randn(2i,2i)
           vA=@view A[1:i,1:i]
           C=randn(i,i)
           @btime cp!($vA,$C)
       end
  981.100 μs (0 allocations: 0 bytes)

julia>

```

compared to `2.49 ms` in the OP.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2025, 4:35pm UTC](https://discourse.julialang.org/t/counter-intuitive-performance-difference/127477/8 "2025-03-30T16:35:14Z")

</div>

I’m guessing that’s a version of the broadcast loop because it gets me the 680-690μs timing of the `.= id2.(` benchmark that evades the `identity` branch. For some reason, the `@simd` really matters there; if I take it out, the time more than doubles to 1.554ms. But shouldn’t the non-contiguity of the view prevent SIMD? EDIT: Maybe not, quickly changed the indices of the view to `1:2:2i, 1:2:2i` for less contiguity and it jumped up to 1.2ms with `@simd`…
