Eachindex in map (and broadcast?)


#1

I have a question about the julia implementation of map and broadcast and its relation to eachindex.

In map!, as well as in a few other places, I find the pattern for (i,j) in zip(eachindex(dest),eachindex(A)), i.e.

for (i,j) in zip(eachindex(dest),eachindex(A))
    dest[i] = f(A[j])
end

Isn’t it the point of eachindex to return an iterator that is suitable even for multiple arrays, i.e.

for I in eachindex(dest,A)
    dest[I] = f(A[I])
end

With the current implementation, if both dest and A have IndexStyle( . ) == IndexCartesian, two CartesianRange iterators will have to run, and this necessarily brings along some overhead. In fact, ccopy! uses the pattern RB, RA = eachindex(B), eachindex(A) and then checks excplicitly wether RA==RB in order to run the iterator only once.

Even if one of the two has IndexLinear, I assume there is little benefit of using linear indices for one if the other is still indexed using cartesian indices. However, if there is some benefit to having linear indices for only one of the two, I would actually like even more if eachindex(dest,A) returns an iterator that produces two separate indices, which could be the same or could be different, so that the above map! implementation would become:

for (i,j) in eachindex(dest,A)
    dest[i] = f(A[j])
end

I think this has several benefits. If both are sparse, the indices could visit all locations that are zero in either one of the two (or more) arrays.

My use case is to have two arrays which have a strided memory layout, but with strides that are not necessarily increasing (not column major), e.g. resulting from a lazy permutedims. I have written a plain iterator that generates a cache efficient memory accessing pattern (i.e. by running first over some outer blocks and than over the indices within each block). Ideally, wrapping these strided arrays in a new Array type, and extending eachindex in this way, map etc would work out of the box, but with the current map! code this is not true.

And my final questions is, how does broadcast fit in. broadcast does not seem to be using eachindex at all, though I am still studying the implementation. Some blog post about the implementation of the current broadcast mechanism might be nice/interesting.


#2

This doesn’t work if one of the arrays is an OffsetArray, e.g. with zero-based indexing.


#3

Hence the proposal to change the semantics of eachindex with multiple arguments, which is more powerful in any respect.

(Though I guess with OffsetArray, one could discuss when arrays can be considered to have the same “domain”, such that they can be copied into each other etc.)


#4

See these issues:
https://github.com/JuliaLang/julia/issues/15648
https://github.com/JuliaLang/julia/pull/15356


#5

That’s great; thanks. I should have looked some further.