From 9e0ab12c36bf16414cf216a82cfeefac5bd2828b Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 27 Jul 2026 11:07:03 +0200 Subject: [PATCH 1/5] Add tests for new mapreduce_scalar bypass --- test/gpu.jl | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/gpu.jl b/test/gpu.jl index 596ca00..99828af 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -184,3 +184,47 @@ end @test compare(x -> prod(exp, x), AT, A3) end end + +@testset "0-dimensional (scalar) StridedView ($AT)" for AT in ATs + @testset for T in (Float32, Float64, ComplexF32, ComplexF64) + R = fill(rand(T)) # 0-dimensional Array + A = StridedView(AT(R)) + @test ndims(A) == 0 + + # full reductions + @test sum(A) == sum(R) + @test prod(A) == prod(R) + @test mapreduce(abs2, +, A) == mapreduce(abs2, +, R) + @test maximum(abs, A) == maximum(abs, R) + @test minimum(abs, A) == minimum(abs, R) + @test sum(abs2, A) == sum(abs2, R) + @test mapreduce(identity, +, A; init = one(T)) == + mapreduce(identity, +, R; init = one(T)) + + # map / map! / copy! / fill! + @test map(x -> 2x, A)[] == 2 * R[] + B = StridedView(AT(fill(zero(T)))) + map!(x -> x + one(T), B, A) + @test B[] == R[] + one(T) + copy!(B, A) + @test B[] == R[] + fill!(B, one(T)) + @test B[] == one(T) + + # offset handling: 0-dim views into a larger parent + Psrc = rand(T, 5) + Pdst = rand(T, 5) + s = sreshape(StridedView(AT(Psrc))[4:4], ()) + d = sreshape(StridedView(AT(Pdst))[3:3], ()) + @test sum(s) == Psrc[4] + copy!(d, s) + @test Pdst[3] == Psrc[4] + + # low-level in-place reduction with a custom initop + Pd = rand(T, 5) + d2 = sreshape(StridedView(AT(Pd))[2:2], ()) + prev = Pd[2] + Strided._mapreducedim!(sin, +, identity, (), (d2, A)) + @test Pd[2] == prev + sin(R[]) + end +end From e69bfe10d2e7a18668fdaa73f06c2ce5ff7814cf Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 27 Jul 2026 11:52:43 +0200 Subject: [PATCH 2/5] Fix and test --- ext/StridedGPUArraysExt.jl | 18 +++++++++++++++ test/gpu.jl | 45 ++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/ext/StridedGPUArraysExt.jl b/ext/StridedGPUArraysExt.jl index b65313d..b365310 100644 --- a/ext/StridedGPUArraysExt.jl +++ b/ext/StridedGPUArraysExt.jl @@ -105,6 +105,24 @@ function Strided._mapreduce( return Array(out)[1] end +# 0-dimensional fast path: bypass @generated kernel +# needs GPU specific extension to avoid scalar indexing error +function Strided._mapreduce_scalar!(@nospecialize(f), @nospecialize(op), @nospecialize(initop), + arrays::Tuple{GPUStridedView{TO, 0}, Vararg{GPUStridedView{<:Any, 0}}}) where {TO} + out = arrays[1] + iout = ParentIndex(Strided.offset(out) + 1) + @allowscalar begin + v = f(map(a -> a[ParentIndex(Strided.offset(a) + 1)], Base.tail(arrays))...) + if op === nothing + out[iout] = v + else + o = initop === nothing ? out[iout] : initop(out[iout]) + out[iout] = op(o, v) + end + end + return nothing +end + function Strided._mapreduce_block!( f, op, initop, dims::Dims{N}, diff --git a/test/gpu.jl b/test/gpu.jl index 99828af..973f96d 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -202,29 +202,46 @@ end mapreduce(identity, +, R; init = one(T)) # map / map! / copy! / fill! - @test map(x -> 2x, A)[] == 2 * R[] + mapx = map(x -> 2x, A) + GPUArrays.@allowscalar begin + @test mapx[] == 2 * R[] + end B = StridedView(AT(fill(zero(T)))) map!(x -> x + one(T), B, A) - @test B[] == R[] + one(T) + GPUArrays.@allowscalar begin + @test B[] == collect(R)[] + one(T) + end copy!(B, A) - @test B[] == R[] + GPUArrays.@allowscalar begin + @test B[] == R[] + end fill!(B, one(T)) - @test B[] == one(T) + GPUArrays.@allowscalar begin + @test B[] == one(T) + end # offset handling: 0-dim views into a larger parent - Psrc = rand(T, 5) - Pdst = rand(T, 5) - s = sreshape(StridedView(AT(Psrc))[4:4], ()) - d = sreshape(StridedView(AT(Pdst))[3:3], ()) - @test sum(s) == Psrc[4] + Psrc = AT(rand(T, 5)) + Pdst = AT(rand(T, 5)) + s = sreshape(StridedView(Psrc)[4:4], ()) + d = sreshape(StridedView(Pdst)[3:3], ()) + GPUArrays.@allowscalar begin + @test sum(s) == Psrc[4] + end copy!(d, s) - @test Pdst[3] == Psrc[4] + GPUArrays.@allowscalar begin + @test Pdst[3] == Psrc[4] + end # low-level in-place reduction with a custom initop - Pd = rand(T, 5) - d2 = sreshape(StridedView(AT(Pd))[2:2], ()) - prev = Pd[2] + Pd = AT(rand(T, 5)) + d2 = sreshape(StridedView(Pd)[2:2], ()) + GPUArrays.@allowscalar begin + prev = Pd[2] + end Strided._mapreducedim!(sin, +, identity, (), (d2, A)) - @test Pd[2] == prev + sin(R[]) + GPUArrays.@allowscalar begin + @test Pd[2] == prev + sin(R[]) + end end end From f35fc11765aa91d85f02c56c8c0020f5ca705a2b Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 27 Jul 2026 11:53:28 +0200 Subject: [PATCH 3/5] Formatter --- ext/StridedGPUArraysExt.jl | 6 ++++-- test/gpu.jl | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/StridedGPUArraysExt.jl b/ext/StridedGPUArraysExt.jl index b365310..7525b44 100644 --- a/ext/StridedGPUArraysExt.jl +++ b/ext/StridedGPUArraysExt.jl @@ -107,8 +107,10 @@ end # 0-dimensional fast path: bypass @generated kernel # needs GPU specific extension to avoid scalar indexing error -function Strided._mapreduce_scalar!(@nospecialize(f), @nospecialize(op), @nospecialize(initop), - arrays::Tuple{GPUStridedView{TO, 0}, Vararg{GPUStridedView{<:Any, 0}}}) where {TO} +function Strided._mapreduce_scalar!( + @nospecialize(f), @nospecialize(op), @nospecialize(initop), + arrays::Tuple{GPUStridedView{TO, 0}, Vararg{GPUStridedView{<:Any, 0}}} + ) where {TO} out = arrays[1] iout = ParentIndex(Strided.offset(out) + 1) @allowscalar begin diff --git a/test/gpu.jl b/test/gpu.jl index 973f96d..1015a88 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -185,7 +185,7 @@ end end end -@testset "0-dimensional (scalar) StridedView ($AT)" for AT in ATs +@testset "0-dimensional (scalar) StridedView ($AT)" for AT in ATs @testset for T in (Float32, Float64, ComplexF32, ComplexF64) R = fill(rand(T)) # 0-dimensional Array A = StridedView(AT(R)) From 180daca9322fbe89806e1b26ae17241d6262cb2a Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 27 Jul 2026 12:16:07 +0200 Subject: [PATCH 4/5] No fp64 for Metal --- test/gpu.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gpu.jl b/test/gpu.jl index 1015a88..cf08ee5 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -186,7 +186,7 @@ end end @testset "0-dimensional (scalar) StridedView ($AT)" for AT in ATs - @testset for T in (Float32, Float64, ComplexF32, ComplexF64) + @testset for T in (Float32, ComplexF32) R = fill(rand(T)) # 0-dimensional Array A = StridedView(AT(R)) @test ndims(A) == 0 From 84f9afa31aaa8a789a5441f1815ba2cadb5fd933 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 27 Jul 2026 15:40:54 +0200 Subject: [PATCH 5/5] Update Project.toml Bump patch version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a0f9ab3..c483aaa 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Strided" uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" -version = "2.6.3" +version = "2.6.4" authors = ["Lukas Devos ", "Maarten Van Damme ", "Jutho Haegeman "] [deps]