From 23628266e0cefbee6086ee59677dc4757d220494 Mon Sep 17 00:00:00 2001 From: pfackeldey Date: Thu, 31 Jul 2025 09:04:53 -0400 Subject: [PATCH 1/5] perf: add specializations for mapreduce min/max --- src/vector_of_arrays.jl | 4 ++++ test/runtests.jl | 2 +- test/vector_of_arrays.jl | 31 +++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/vector_of_arrays.jl b/src/vector_of_arrays.jl index 645a9e2..a71ce83 100644 --- a/src/vector_of_arrays.jl +++ b/src/vector_of_arrays.jl @@ -337,6 +337,10 @@ function Base.append!(A::VectorOfArrays{T,N}, B::AbstractVector{<:AbstractArray{ end +Base.mapreduce(::typeof(maximum), ::typeof(max), V::VectorOfArrays; kw...) = maximum(V.data; kw...) +Base.mapreduce(::typeof(minimum), ::typeof(min), V::VectorOfArrays; kw...) = minimum(V.data; kw...) + + Base.vcat(V::VectorOfArrays) = V function Base.vcat(Vs::(VectorOfArrays{U,N} where U)...) where {N} diff --git a/test/runtests.jl b/test/runtests.jl index fa08bb9..33f956c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ import Test -Test.@testset "Package ArraysOfArrays" begin +Test.@testset verbose=true "Package ArraysOfArrays" begin include("test_aqua.jl") include("functions.jl") include("array_of_similar_arrays.jl") diff --git a/test/vector_of_arrays.jl b/test/vector_of_arrays.jl index 8ea4d00..7e856a6 100644 --- a/test/vector_of_arrays.jl +++ b/test/vector_of_arrays.jl @@ -76,6 +76,37 @@ using ArraysOfArrays: full_consistency_checks, append_elemptr!, element_ptr end + @testset "mapreduce maximum/minimum shortcut" begin + A1 = ref_AoA3(Float32, 3); A2 = ref_AoA3(Float32, 0) + A3 = ref_AoA3(Float32, 4); A4 = ref_AoA3(Float64, 2) + + B1 = VectorOfArrays(A1); B2 = VectorOfArrays(A2); + B3 = VectorOfArrays(A3); B4 = VectorOfArrays(A4); + + @testset "maximum - correctness" begin + @test mapreduce(maximum, max, B1) == maximum(B1.data) + @test mapreduce(maximum, max, B2; init=Float32(0.)) == maximum(B2.data; init=Float32(0.)) + @test mapreduce(maximum, max, B3) == maximum(B3.data) + @test mapreduce(maximum, max, B4) == maximum(B4.data) + end + + @testset "maximum - performance" begin + @test (@allocated mapreduce(maximum, max, B1)) == (@allocated maximum(B1.data)) + end + + @testset "minimum - correctness" begin + @test mapreduce(minimum, min, B1) == minimum(B1.data) + @test mapreduce(minimum, min, B2; init=Float32(0.)) == minimum(B2.data; init=Float32(0.)) + @test mapreduce(minimum, min, B3) == minimum(B3.data) + @test mapreduce(minimum, min, B4) == minimum(B4.data) + end + + @testset "minimum - performance" begin + @test (@allocated mapreduce(minimum, min, B1)) == (@allocated minimum(B1.data)) + end + end + + @testset "append! and vcat" begin A1 = ref_AoA3(Float32, 3); A2 = ref_AoA3(Float32, 0) A3 = ref_AoA3(Float32, 4); A4 = ref_AoA3(Float64, 2) From 2dd33cab5477dc769c8b923d0fb0a0259e0a37a0 Mon Sep 17 00:00:00 2001 From: Peter Fackeldey Date: Thu, 31 Jul 2025 09:36:56 -0400 Subject: [PATCH 2/5] Update src/vector_of_arrays.jl Co-authored-by: Oliver Schulz --- src/vector_of_arrays.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vector_of_arrays.jl b/src/vector_of_arrays.jl index a71ce83..0e3f06e 100644 --- a/src/vector_of_arrays.jl +++ b/src/vector_of_arrays.jl @@ -337,8 +337,8 @@ function Base.append!(A::VectorOfArrays{T,N}, B::AbstractVector{<:AbstractArray{ end -Base.mapreduce(::typeof(maximum), ::typeof(max), V::VectorOfArrays; kw...) = maximum(V.data; kw...) -Base.mapreduce(::typeof(minimum), ::typeof(min), V::VectorOfArrays; kw...) = minimum(V.data; kw...) +Base.mapreduce(::typeof(maximum), ::typeof(max), V::VectorOfArrays; kw...) = maximum(flatview(V); kw...) +Base.mapreduce(::typeof(minimum), ::typeof(min), V::VectorOfArrays; kw...) = minimum(flatview(V); kw...) Base.vcat(V::VectorOfArrays) = V From 06f36bbbddbaab8274f97864020869a84a8b9a55 Mon Sep 17 00:00:00 2001 From: pfackeldey Date: Thu, 31 Jul 2025 10:37:34 -0400 Subject: [PATCH 3/5] add specialization also to ArrayOfSimilarArrays --- src/array_of_similar_arrays.jl | 3 +++ test/array_of_similar_arrays.jl | 11 +++++++++++ test/vector_of_arrays.jl | 20 ++++++++++---------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/array_of_similar_arrays.jl b/src/array_of_similar_arrays.jl index 7d6a0b2..8246729 100644 --- a/src/array_of_similar_arrays.jl +++ b/src/array_of_similar_arrays.jl @@ -262,6 +262,9 @@ Base.@propagate_inbounds function _deepview_impl_aosa(A::ArrayOfSimilarArrays, i end +Base.mapreduce(::typeof(maximum), ::typeof(max), A::ArrayOfSimilarArrays; kw...) = maximum(flatview(A); kw...) +Base.mapreduce(::typeof(minimum), ::typeof(min), A::ArrayOfSimilarArrays; kw...) = minimum(flatview(A); kw...) + const VectorOfSimilarArrays{ T, M, L, diff --git a/test/array_of_similar_arrays.jl b/test/array_of_similar_arrays.jl index 4c2ca84..4483fac 100644 --- a/test/array_of_similar_arrays.jl +++ b/test/array_of_similar_arrays.jl @@ -365,6 +365,17 @@ using StatsBase: cov2cor @test @inferred(ArraysOfArrays._innerlength(VSV)) == N end + @testset "mapreduce maximum/minimum shortcut" begin + r1 = rand(1,4); r2 = rand(1,4); r3 = rand(1,4); r4 = rand(1,4) + ASA = ArrayOfSimilarArrays([r1,r2,r3,r4]) + + @test mapreduce(maximum, max, ASA) == maximum(flatview(ASA)) + @test (@allocated mapreduce(maximum, max, ASA)) == (@allocated maximum(flatview(ASA))) + + @test mapreduce(minimum, min, ASA) == minimum(flatview(ASA)) + @test (@allocated mapreduce(minimum, min, ASA)) == (@allocated minimum(flatview(ASA))) + end + @testset "map and broadcast" begin A_flat = rand(2,3,4,5,6) A = nestedview(A_flat, 2) diff --git a/test/vector_of_arrays.jl b/test/vector_of_arrays.jl index 7e856a6..0a5569e 100644 --- a/test/vector_of_arrays.jl +++ b/test/vector_of_arrays.jl @@ -84,25 +84,25 @@ using ArraysOfArrays: full_consistency_checks, append_elemptr!, element_ptr B3 = VectorOfArrays(A3); B4 = VectorOfArrays(A4); @testset "maximum - correctness" begin - @test mapreduce(maximum, max, B1) == maximum(B1.data) - @test mapreduce(maximum, max, B2; init=Float32(0.)) == maximum(B2.data; init=Float32(0.)) - @test mapreduce(maximum, max, B3) == maximum(B3.data) - @test mapreduce(maximum, max, B4) == maximum(B4.data) + @test mapreduce(maximum, max, B1) == maximum(flatview(B1)) + @test mapreduce(maximum, max, B2; init=Float32(0.)) == maximum(flatview(B2); init=Float32(0.)) + @test mapreduce(maximum, max, B3) == maximum(flatview(B3)) + @test mapreduce(maximum, max, B4) == maximum(flatview(B4)) end @testset "maximum - performance" begin - @test (@allocated mapreduce(maximum, max, B1)) == (@allocated maximum(B1.data)) + @test (@allocated mapreduce(maximum, max, B1)) == (@allocated maximum(flatview(B1))) end @testset "minimum - correctness" begin - @test mapreduce(minimum, min, B1) == minimum(B1.data) - @test mapreduce(minimum, min, B2; init=Float32(0.)) == minimum(B2.data; init=Float32(0.)) - @test mapreduce(minimum, min, B3) == minimum(B3.data) - @test mapreduce(minimum, min, B4) == minimum(B4.data) + @test mapreduce(minimum, min, B1) == minimum(flatview(B1)) + @test mapreduce(minimum, min, B2; init=Float32(0.)) == minimum(flatview(B2); init=Float32(0.)) + @test mapreduce(minimum, min, B3) == minimum(flatview(B3)) + @test mapreduce(minimum, min, B4) == minimum(flatview(B4)) end @testset "minimum - performance" begin - @test (@allocated mapreduce(minimum, min, B1)) == (@allocated minimum(B1.data)) + @test (@allocated mapreduce(minimum, min, B1)) == (@allocated minimum(flatview(B1))) end end From 387574c49fe22080c99bfa3fdf62a2c552f27aa9 Mon Sep 17 00:00:00 2001 From: pfackeldey Date: Thu, 31 Jul 2025 10:59:11 -0400 Subject: [PATCH 4/5] improve tests --- test/vector_of_arrays.jl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/vector_of_arrays.jl b/test/vector_of_arrays.jl index 0a5569e..44c658d 100644 --- a/test/vector_of_arrays.jl +++ b/test/vector_of_arrays.jl @@ -84,25 +84,29 @@ using ArraysOfArrays: full_consistency_checks, append_elemptr!, element_ptr B3 = VectorOfArrays(A3); B4 = VectorOfArrays(A4); @testset "maximum - correctness" begin - @test mapreduce(maximum, max, B1) == maximum(flatview(B1)) - @test mapreduce(maximum, max, B2; init=Float32(0.)) == maximum(flatview(B2); init=Float32(0.)) - @test mapreduce(maximum, max, B3) == maximum(flatview(B3)) - @test mapreduce(maximum, max, B4) == maximum(flatview(B4)) + @test mapreduce(maximum, max, B1) == mapreduce(maximum, max, Array(B1)) + @test mapreduce(maximum, max, B2; init=Float32(0.)) == mapreduce(maximum, max, Array(B2); init=Float32(0.)) + @test mapreduce(maximum, max, B3) == mapreduce(maximum, max, Array(B3)) + @test mapreduce(maximum, max, B4) == mapreduce(maximum, max, Array(B4)) end @testset "maximum - performance" begin - @test (@allocated mapreduce(maximum, max, B1)) == (@allocated maximum(flatview(B1))) + B1_naive = Array(B1) + mapreduce(maximum, max, B1_naive) + @test (@allocated mapreduce(maximum, max, B1)) <= (@allocated mapreduce(maximum, max, B1_naive)) end @testset "minimum - correctness" begin - @test mapreduce(minimum, min, B1) == minimum(flatview(B1)) - @test mapreduce(minimum, min, B2; init=Float32(0.)) == minimum(flatview(B2); init=Float32(0.)) - @test mapreduce(minimum, min, B3) == minimum(flatview(B3)) - @test mapreduce(minimum, min, B4) == minimum(flatview(B4)) + @test mapreduce(minimum, min, B1) == mapreduce(minimum, min, Array(B1)) + @test mapreduce(minimum, min, B2; init=Float32(0.)) == mapreduce(minimum, min, Array(B2); init=Float32(0.)) + @test mapreduce(minimum, min, B3) == mapreduce(minimum, min, Array(B3)) + @test mapreduce(minimum, min, B4) == mapreduce(minimum, min, Array(B4)) end @testset "minimum - performance" begin - @test (@allocated mapreduce(minimum, min, B1)) == (@allocated minimum(flatview(B1))) + B1_naive = Array(B1) + mapreduce(minimum, min, B1_naive) + @test (@allocated mapreduce(minimum, min, B1)) <= (@allocated mapreduce(minimum, min, B1_naive)) end end From ffe60300c1d561991c765ebc6cf38c2e1e4eb59a Mon Sep 17 00:00:00 2001 From: pfackeldey Date: Fri, 1 Aug 2025 11:38:55 -0400 Subject: [PATCH 5/5] remove kwargs forwarding --- src/vector_of_arrays.jl | 4 ++-- test/vector_of_arrays.jl | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vector_of_arrays.jl b/src/vector_of_arrays.jl index 0e3f06e..d7c3724 100644 --- a/src/vector_of_arrays.jl +++ b/src/vector_of_arrays.jl @@ -337,8 +337,8 @@ function Base.append!(A::VectorOfArrays{T,N}, B::AbstractVector{<:AbstractArray{ end -Base.mapreduce(::typeof(maximum), ::typeof(max), V::VectorOfArrays; kw...) = maximum(flatview(V); kw...) -Base.mapreduce(::typeof(minimum), ::typeof(min), V::VectorOfArrays; kw...) = minimum(flatview(V); kw...) +Base.mapreduce(::typeof(maximum), ::typeof(max), V::VectorOfArrays) = maximum(flatview(V)) +Base.mapreduce(::typeof(minimum), ::typeof(min), V::VectorOfArrays) = minimum(flatview(V)) Base.vcat(V::VectorOfArrays) = V diff --git a/test/vector_of_arrays.jl b/test/vector_of_arrays.jl index 44c658d..6e3ae58 100644 --- a/test/vector_of_arrays.jl +++ b/test/vector_of_arrays.jl @@ -77,15 +77,16 @@ using ArraysOfArrays: full_consistency_checks, append_elemptr!, element_ptr @testset "mapreduce maximum/minimum shortcut" begin - A1 = ref_AoA3(Float32, 3); A2 = ref_AoA3(Float32, 0) + A1 = ref_AoA3(Float32, 3); # A2 = ref_AoA3(Float32, 0) A3 = ref_AoA3(Float32, 4); A4 = ref_AoA3(Float64, 2) - B1 = VectorOfArrays(A1); B2 = VectorOfArrays(A2); + B1 = VectorOfArrays(A1); # B2 = VectorOfArrays(A2); B3 = VectorOfArrays(A3); B4 = VectorOfArrays(A4); @testset "maximum - correctness" begin @test mapreduce(maximum, max, B1) == mapreduce(maximum, max, Array(B1)) - @test mapreduce(maximum, max, B2; init=Float32(0.)) == mapreduce(maximum, max, Array(B2); init=Float32(0.)) + # `init` kwarg is not supported for this specialization right now + # @test mapreduce(maximum, max, B2; init=Float32(0.)) == mapreduce(maximum, max, Array(B2); init=Float32(0.)) @test mapreduce(maximum, max, B3) == mapreduce(maximum, max, Array(B3)) @test mapreduce(maximum, max, B4) == mapreduce(maximum, max, Array(B4)) end @@ -98,7 +99,8 @@ using ArraysOfArrays: full_consistency_checks, append_elemptr!, element_ptr @testset "minimum - correctness" begin @test mapreduce(minimum, min, B1) == mapreduce(minimum, min, Array(B1)) - @test mapreduce(minimum, min, B2; init=Float32(0.)) == mapreduce(minimum, min, Array(B2); init=Float32(0.)) + # `init` kwarg is not supported for this specialization right now + # @test mapreduce(minimum, min, B2; init=Float32(0.)) == mapreduce(minimum, min, Array(B2); init=Float32(0.)) @test mapreduce(minimum, min, B3) == mapreduce(minimum, min, Array(B3)) @test mapreduce(minimum, min, B4) == mapreduce(minimum, min, Array(B4)) end