Skip to content

Commit

Permalink
Loop over Iterators.rest in _foldl_impl (#56492)
Browse files Browse the repository at this point in the history
For reasons that I don't understand, this improves performance in
`mapreduce` in the following example:
```julia
julia> function g(A)
           for col in axes(A,2)
               mapreduce(iszero, &, view(A, UnitRange(axes(A,1)), col), init=true) || return false
           end
           return true
       end
g (generic function with 2 methods)

julia> A = zeros(2, 10000);

julia> @Btime g($A);
  28.021 μs (0 allocations: 0 bytes) # nightly v"1.12.0-DEV.1571"
  12.462 μs (0 allocations: 0 bytes) # this PR

julia> A = zeros(1000,1000);

julia> @Btime g($A);
  372.080 μs (0 allocations: 0 bytes) # nightly
  321.753 μs (0 allocations: 0 bytes) # this PR
```
It would be good to understand what the underlying issue is, as the two
seem equivalent to me. Perhaps this form makes it clear that it's not,
in fact, an infinite loop?
  • Loading branch information
jishnub authored Nov 9, 2024
1 parent 473d0db commit 024d42a
Showing 1 changed file with 6 additions and 6 deletions.
12 changes: 6 additions & 6 deletions base/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ function foldl_impl(op::OP, nt, itr) where {OP}
end

function _foldl_impl(op::OP, init, itr) where {OP}
# Unroll the while loop once; if init is known, the call to op may
# be evaluated at compile time
# Unroll the loop once to check if the iterator is empty.
# If init is known, the call to op may be evaluated at compile time
y = iterate(itr)
y === nothing && return init
v = op(init, y[1])
while true
y = iterate(itr, y[2])
y === nothing && break
v = op(v, y[1])
# Using a for loop is more performant than a while loop (see #56492)
# This unrolls the loop a second time before entering the body
for x in Iterators.rest(itr, y[2])
v = op(v, x)
end
return v
end
Expand Down

0 comments on commit 024d42a

Please sign in to comment.