Is it safe to broadcast `setindex!` with views of the same array?

I tried to find out looking here and through the documentation, but I couldn’t find a concrete answer so far:

Is it safe to re-order array elements in-place using a view into the same array, like so:

julia> A = rand(100);
julia> B = sort(A);
julia> order = sortperm(A);

# This is what I'm asking about:
julia> A .= view(A, order)

julia> A == B # true

I think the answer is “yes”, because it just seems to work correctly when I tested it (A == B is true in the above example) and A .= view(A, order) seems to allocate, whereas A .= view(B, order) doesn’t. It makes sense that this cannot be done without any allocations, since the array cannot be overwritten in-place without storing the right elements somewhere.


Follow-up: Does it make sense to do that (or is there a more efficient way to achieve that re-ordering in the first place)? It seems like the allocations for A .= view(A, order) are actually always about twice as much as from just doing A .= A[order], so it’s probably fastest to just pre-allocate a buffer and use it to store the right order (if I have to repeat this for many large arrays)?

buffer = similar(A)
buffer .= view(A, order)
A .= buffer

I was just wondering if there is a one-liner that can do that somehow (minus the pre-allocation).

It seems to me like you are looking for permute!.

1 Like

Thank you! I actually missed that function :sweat_smile:

Yes, that’s the functionality I ultimately need, but the docstring of permute! mentions essentially the same performance limitations as described above.

To return a new permutation, use v[p]. This is generally faster than permute!(v, p); it is even faster to write into a pre-allocated output array with u .= @view v[p]. (Even though permute! overwrites v in-place, it internally requires some allocation to keep track of which elements have been moved.)

So it sounds the approach of pre-allocating, copying the permutation with buffer .= view(A, order) and then copying again to the original vector is as fast as it gets.

2 Likes

Yeah, permute! is just surprisingly hard to do without allocations. You can do slightly better if you allow it to also mess up the order as it does its work, but it’s not a panacea. I don’t know if these implementations ever found a new home, but you could try them (if it’s ok for you to overwrite order).

1 Like

That’s very interesting, thanks for the link!

Unfortunately I have to re-use the same order for a few different arrays at every iteration (and repeat the process for many iterations, each with a new order), so I could either re-generate the same order a few times or just stick to the current version with copying memory around between buffers. The latter sounds like it would be faster, but I can compare the two approaches.