reduce
(without init
keyword) is much faster than foldl
and foldr
:
julia> v = rand(Int, 2^16); w = @view v[2:2:end];
julia> @btime reduce(+, $w);
12.154 μs (0 allocations: 0 bytes)
julia> @btime foldl(+, $w);
190.207 μs (0 allocations: 0 bytes)
The same applies to mapreduce
. I believe the reason is that internally _mapreduce
uses @inbounds
:
EDIT: For arrays with more than 16 elements, it’s actually the function mapreduce_impl
, which again uses @inbounds
.
However, the indices array inds
is initialized at the beginning of the function, so that the code won’t detect if the array A
is modified via f
or op
. This may lead to an out-of-bounds access:
julia> v = [1,2,3,4];
julia> reduce(v) do x, y
@show (x, y, v)
empty!(v)
x+y
end
(x, y, v) = (1, 2, [1, 2, 3, 4])
(x, y, v) = (3, 3, Int64[])
(x, y, v) = (6, 4, Int64[])
10
For foldl
and foldr
this does not happen:
julia> v = [1,2,3,4];
julia> foldl(v) do x, y
@show (x, y, v)
empty!(v)
x+y
end
(x, y, v) = (1, 2, [1, 2, 3, 4])
3
I wonder whether this use of @inbounds
is considered safe (or safe enough). It has obvious risks, but on the other hand one may argue that it is comparable to views. The docstring for view
says:
The behavior is undefined if the shape of the parent array is changed after
view
is called because there is no bound check for the parent array; e.g., it may cause a segmentation fault.
If it is considered safe, then one could speed up foldl
and foldr
in the same way.