Closed KristofferC closed 10 months ago
Bisected to 17a3c7702e2cb20171d1211606343fc50533a588 cc @johnnychen94
Just FYI, the same workaround as #38086 is available. However, this issue is not the issue with SIMD ( simd_index
). The @simd
here only assists in loop unrolling (and/or LICM), not vectorization.
function perf_sumcartesian_simd(A)
s = zero(eltype(A))
@inbounds @simd for I in CartesianIndices(size(A))
val = Base.unsafe_getindex(A, I)
s += val
end
return s
end
julia> @btime perf_sumcartesian_simd($A)
6.000 ns (0 allocations: 0 bytes)
Changing the implementation of the iterator will change the optimization result.
However, I cannot find any problem with the current implementation of iterate
in terms of inlining cost, type stability or bounds checking.
using Base.IteratorsMD: __inc
@inline function Base.iterate(iter::CartesianIndices{1}, state) # FIXME: this is just for experimentation
next = iterate(iter.indices[1], state.I[1])
next === nothing && return nothing
return CartesianIndex(next[1]), CartesianIndex(next[1])
end
Edit This is just for experimentation, but I think it might be a good idea to delegate all the calculations of range and step to the iterators. However, since there is no guarantee that the index and state will match, each must be managed separately. That can increase the overhead in high dimensional indices.
It does not seem to be optimized when using __is_valid_range
. However, it is better to have it for safety. Iterators such as UnitRange
are not range-checked, though.
https://github.com/JuliaLang/julia/blob/7853ddda3481dac54fe9f1b77d47721476afa295/base/multidimensional.jl#L412-L420
julia> iterate(1:10, -100)
(-99, -99)
julia> iterate(1:10, 100)
(101, 101)
Edit: The implementation of state is not public, so I don't think we need to take care of the cases where the user breaks the state.
This is just for experimentation, but I think it might be a good idea to delegate all the calculations of range and step to the iterators.
The implementation is quite simple. However, it is unclear whether this breaking change could be widely accepted. (Edit: Of course, I won't be proposing this for v1.6.)
cc: @johnnychen94, @timholy
Edit2:
Hold on a second. Isn't this a reinvention of ProductIterator
? :joy:
:sweat_smile:
using Base.Iterators
using Base.Iterators: ProductIterator
@inline function iterate(iter::CartesianIndices, state=nothing)
p = ProductIterator(iter.indices)
next = state === nothing ? iterate(p) : iterate(p, state)
next === nothing && return nothing
return CartesianIndex(next[1]), next[2]
end
xref: #35074, #35036, #36832
Still reproduces on 1.8.3 and 1.9.0-alpha1
I believe no longer reproduces
Code to repro:
On master:
On 1.5: