Hi,
Why does the second version allocate ? Is there a clean way to do that without allocating ?
z = collect(1:10000)
function test1(z)
z .-= 1
end
4.898 μs (0 allocations: 0 bytes)
function test2(z)
z[56:end] .-= 1
end
20.458 μs (3 allocations: 77.84 KiB)
function test3(z)
for i in 56:length(z)
z[i] -= 1
end
end
14.115 μs (0 allocations: 0 bytes)
Of course that is an argument, but slices on the left are different, so that could be different as well. But it may be that parsing that becomes impossible when one goes to more general cases.
It would be a breaking change. e.g. it would change the answer in:
x = [3,2,1]
x[1:2] .-= sort!(x)[1:2]
There was a long debate early in the history of Julia about whether we should change slices to default to views (range indexing should produce a subarray, not a copy · Issue #3701 · JuliaLang/julia · GitHub). Ultimately it was decided not to change because (a) attempting to change the default turned out to be a source of too many subtle breaking bugs, (b) it wasn’t always a performance win, and (c) the introduction of @views made it relatively painless to opt-in for a large block of code, e.g. a whole function.
That being said, it’s possible that the compiler could at some point recognize that it can safely replace copies with views in certain cases, as commented by @StefanKarpinskihere. Doing so would probably privilege “built-in” array types and functions that the compiler can analyze easily, however, and the design of Julia has always favored optimizations (e.g. loop fusing for dot operations) and syntax (e.g. @views) that apply equally well to user-constructed types and functions.
I feel like the at least literal values on the rhs of .+= should be optimized. Even if we just do a patchy work in, say, parser/lowering stage would be helpful.
Hard case that requires analysis in compiler passes can be deferred
I find it hard to believe that array .+= literal is a performance-critical step in much real-world code, to the point of being worth special-casing in the compiler.