From e785906d8aad83dc0142176f5b7be80b4f805765 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 30 Sep 2025 17:47:28 -0400 Subject: [PATCH] fix performance of ProductIterator for non-concrete iterators This uses inferred element types to make the iterator state type-stable, enabling inlining, which can enable the runtime to see the real types. Fix #56607 julia> @btime f() 574.500 ns (2 allocations: 272 bytes) # non-const op 253.878 ns (2 allocations: 272 bytes) # const op --- base/iterators.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/base/iterators.jl b/base/iterators.jl index 5909608709e36..5b061f60573b1 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -16,7 +16,7 @@ using .Base: (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, =>, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString, - afoldl, mod1 + afoldl, mod1, @default_eltype using .Core using Core: @doc @@ -1176,6 +1176,8 @@ end next === nothing && return nothing restnext = _piterate(rest...) restnext === nothing && return nothing + VS = @default_eltype(iter1) + next = Pair{VS, typeof(next[2])}(next[1], next[2]) return (next, restnext...) end @inline function iterate(P::ProductIterator) @@ -1188,8 +1190,8 @@ end @inline _piterate1(::Tuple{}, ::Tuple{}) = nothing @inline function _piterate1(iters, states) iter1 = first(iters) - next = iterate(iter1, first(states)[2]) - restnext = tail(states) + state1, restnext... = states + next = iterate(iter1, state1[2]) if next === nothing isdone(iter1) === true && return nothing restnext = _piterate1(tail(iters), restnext) @@ -1197,6 +1199,7 @@ end next = iterate(iter1) next === nothing && return nothing end + next = Pair{fieldtype(typeof(state1), 1), typeof(next[2])}(next[1], next[2]) return (next, restnext...) end @inline function iterate(P::ProductIterator, states)