Skip to content

Commit

Permalink
Fix #28849: faster maximum, minimum (#30320)
Browse files Browse the repository at this point in the history
  • Loading branch information
jw3126 authored and ViralBShah committed Dec 18, 2018
1 parent 8d8b3d9 commit 7b2db8e
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 8 deletions.
60 changes: 52 additions & 8 deletions base/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -452,19 +452,63 @@ julia> prod(1:20)
prod(a) = mapreduce(identity, mul_prod, a)

## maximum & minimum
function _fast(::typeof(max),x,y)
ifelse(isnan(x),
x,
ifelse(x > y, x, y))
end

function _fast(::typeof(min),x,y)
ifelse(isnan(x),
x,
ifelse(x < y, x, y))
end

isbadzero(::typeof(max), x::AbstractFloat) = (x == zero(x)) & signbit(x)
isbadzero(::typeof(min), x::AbstractFloat) = (x == zero(x)) & !signbit(x)
isbadzero(op, x) = false
isgoodzero(::typeof(max), x) = isbadzero(min, x)
isgoodzero(::typeof(min), x) = isbadzero(max, x)

function mapreduce_impl(f, op::Union{typeof(max), typeof(min)},
A::AbstractArray, first::Int, last::Int)
# locate the first non NaN number
@inbounds a1 = A[first]
v = mapreduce_first(f, op, a1)
i = first + 1
while (v == v) && (i <= last)
a1 = @inbounds A[first]
v1 = mapreduce_first(f, op, a1)
v2 = v3 = v4 = v1
chunk_len = 256
start = first
stop = start + chunk_len - 4
while stop <= last
isnan(v1) && return v1
isnan(v2) && return v2
isnan(v3) && return v3
isnan(v4) && return v4
@inbounds for i in start:4:stop
v1 = _fast(op, v1, f(A[i+1]))
v2 = _fast(op, v2, f(A[i+2]))
v3 = _fast(op, v3, f(A[i+3]))
v4 = _fast(op, v4, f(A[i+4]))
end
start = stop
stop = start + chunk_len - 4
end
v = op(op(v1,v2),op(v3,v4))
start += 1
for i in start:last
@inbounds ai = A[i]
v = op(v, f(ai))
i += 1
v = op(v, f(A[i]))
end
v

# enforce correct order of 0.0 and -0.0
# e.g. maximum([0.0, -0.0]) === 0.0
# should hold
if isbadzero(op, v)
for i in first:last
x = @inbounds A[i]
isgoodzero(op,x) && return x
end
end
return v
end

maximum(f, a) = mapreduce(f, max, a)
Expand Down
43 changes: 43 additions & 0 deletions test/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,55 @@ let x = [4,3,5,2]
@test extrema(abs2, x) == (4, 25)
end

@test maximum([-0.,0.]) === 0.0
@test maximum([0.,-0.]) === 0.0
@test maximum([0.,-0.,0.]) === 0.0
@test minimum([-0.,0.]) === -0.0
@test minimum([0.,-0.]) === -0.0
@test minimum([0.,-0.,0.]) === -0.0

@testset "minimum/maximum checks all elements" begin
for N in [2:20;150;300]
for i in 1:N
arr = fill(0., N)
truth = rand()
arr[i] = truth
@test maximum(arr) == truth

truth = -rand()
arr[i] = truth
@test minimum(arr) == truth

arr[i] = NaN
@test isnan(maximum(arr))
@test isnan(minimum(arr))

arr = zeros(N)
@test minimum(arr) === 0.0
@test maximum(arr) === 0.0

arr[i] = -0.0
@test minimum(arr) === -0.0
@test maximum(arr) === 0.0

arr = -zeros(N)
@test minimum(arr) === -0.0
@test maximum(arr) === -0.0
arr[i] = 0.0
@test minimum(arr) === -0.0
@test maximum(arr) === 0.0
end
end
end

@test isnan(maximum([NaN]))
@test isnan(minimum([NaN]))
@test isequal(extrema([NaN]), (NaN, NaN))

@test isnan(maximum([NaN, 2.]))
@test isnan(maximum([2., NaN]))
@test isnan(minimum([NaN, 2.]))
@test isnan(minimum([2., NaN]))
@test isequal(extrema([NaN, 2.]), (NaN,NaN))

@test isnan(maximum([NaN, 2., 3.]))
Expand Down

0 comments on commit 7b2db8e

Please sign in to comment.