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
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:
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).
Maybe some hacky use of MappedArrays 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.)
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).
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ā¦
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.
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.