Skip to content

Commit ad0b5e3

Browse files
authored
Accept iterable mapreduce dims (#87)
1 parent c485695 commit ad0b5e3

4 files changed

Lines changed: 50 additions & 27 deletions

File tree

src/arithmetics.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
sum(
33
src::AbstractArray, backend::Backend=get_backend(src);
44
init=zero(eltype(src)),
5-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
5+
dims=nothing,
66
77
# CPU settings
88
max_tasks=Threads.nthreads(),
@@ -58,7 +58,7 @@ end
5858
prod(
5959
src::AbstractArray, backend::Backend=get_backend(src);
6060
init=one(eltype(src)),
61-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
61+
dims=nothing,
6262
6363
# CPU settings
6464
max_tasks=Threads.nthreads(),
@@ -114,7 +114,7 @@ end
114114
maximum(
115115
src::AbstractArray, backend::Backend=get_backend(src);
116116
init=typemin(eltype(src)),
117-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
117+
dims=nothing,
118118
119119
# CPU settings
120120
max_tasks=Threads.nthreads(),
@@ -170,7 +170,7 @@ end
170170
minimum(
171171
src::AbstractArray, backend::Backend=get_backend(src);
172172
init=typemax(eltype(src)),
173-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
173+
dims=nothing,
174174
175175
# CPU settings
176176
max_tasks=Threads.nthreads(),
@@ -226,7 +226,7 @@ end
226226
count(
227227
[f=identity], src::AbstractArray, backend::Backend=get_backend(src);
228228
init=0,
229-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
229+
dims=nothing,
230230
231231
# CPU settings
232232
max_tasks=Threads.nthreads(),

src/reduce/mapreduce_nd.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Generalized N-dimensional mapreduce for GPU and CPU backends, reducing one or more
2-
# dimensions (`dims::Int` or `dims::Tuple`) of `src` into `dst`.
2+
# dimensions (`dims::Integer` or a collection of integers) of `src` into `dst`.
33
#
44
# Design (see references: CUDA.jl / GPUArrays mapreducedim!, PyTorch Reduce.cuh, CUB):
55
# 1. Canonicalize dims: collapse adjacent dimensions with matching strides into contiguous
@@ -77,7 +77,7 @@ function mapreduce_nd(
7777
f, op, src::MapReduceSource, backend::Backend;
7878
init,
7979
neutral=neutral_element(op, typeof(init)),
80-
dims::Union{Int, Tuple{Vararg{Int}}},
80+
dims,
8181

8282
# CPU settings
8383
max_tasks::Int,
@@ -91,14 +91,17 @@ function mapreduce_nd(
9191
@argcheck 1 <= block_size <= 1024
9292
@argcheck ispow2(block_size)
9393

94-
dims_all = dims isa Int ? (dims,) : dims
95-
96-
if Base.any(d < 1 for d in dims_all)
97-
throw(ArgumentError("region dimension(s) must be ≥ 1, got $dims"))
94+
dims_src = dims isa Number ? (dims,) : dims
95+
dims_buf = Int[]
96+
for d in dims_src
97+
d isa Integer || throw(ArgumentError("reduced dimension(s) must be integers"))
98+
dim = Int(d)
99+
dim < 1 && throw(ArgumentError("region dimension(s) must be ≥ 1, got $d"))
100+
push!(dims_buf, dim)
98101
end
99102

100103
# Match Base: duplicate dims are ignored, e.g. dims=(2,2) behaves like dims=2.
101-
dims_all = Tuple(Base.unique(dims_all))
104+
dims_all = Tuple(Base.unique(dims_buf))
102105

103106
src_sizes = size(src)
104107
ndim = length(src_sizes)

src/reduce/reduce.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ include("mapreduce_nd.jl")
5757
op, src::AbstractArray, backend::Backend=get_backend(src);
5858
init,
5959
neutral=neutral_element(op, typeof(init)),
60-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
60+
dims=nothing,
6161
6262
# CPU settings
6363
max_tasks::Int=Threads.nthreads(),
@@ -70,16 +70,16 @@ include("mapreduce_nd.jl")
7070
)
7171
7272
Reduce `src` along dimensions `dims` using the binary operator `op`. If `dims` is `nothing` or
73-
`:`, reduce `src` to a scalar. If `dims` is an integer or a tuple of integers, reduce `src` along
74-
those dimension(s). The `init` value is used as the initial value for the reduction; `neutral` is
75-
the neutral element for the operator `op`.
73+
`:`, reduce `src` to a scalar. If `dims` is an integer or a collection of integers, reduce `src`
74+
along those dimension(s). The `init` value is used as the initial value for the reduction; `neutral`
75+
is the neutral element for the operator `op`.
7676
7777
The returned type is the same as `init` - to control output precision, specify `init` explicitly.
7878
7979
## CPU settings
8080
Use at most `max_tasks` threads with at least `min_elems` elements per task. For N-dimensional
81-
arrays (`dims` is an integer or tuple) multithreading currently only becomes faster for
82-
`max_tasks >= 4`; all other cases are scaling linearly with the number of threads.
81+
arrays (`dims` is an integer or a collection of integers) multithreading currently only becomes
82+
faster for `max_tasks >= 4`; all other cases are scaling linearly with the number of threads.
8383
8484
Note that multithreading reductions only improves performance for cases with more compute-heavy
8585
operations, which hide the memory latency and thread launch overhead - that includes:
@@ -93,8 +93,8 @@ The `block_size` parameter controls the number of threads per block and must be
9393
9494
The `temp` parameter can be used to pass a pre-allocated temporary array. For reduction to a scalar
9595
(`dims=nothing` or `dims=:`), `length(temp) >= 2 * (length(src) + 2 * block_size - 1) ÷ (2 *
96-
block_size)` is required. For reduction along dimensions (`dims` is an integer or tuple), `temp` is
97-
used as the destination array, and thus must have the exact dimensions required - i.e. same
96+
block_size)` is required. For reduction along dimensions (`dims` is an integer or a collection of
97+
integers), `temp` is used as the destination array, and thus must have the exact dimensions required - i.e. same
9898
dimensionwise sizes as `src`, except for the reduced dimension(s) which become 1; there are some
9999
corner cases when one dimension is zero, check against `Base.reduce` for CPU arrays for exact
100100
behavior.
@@ -142,7 +142,7 @@ end
142142
f, op, src::AbstractArray, backend::Backend=get_backend(src);
143143
init,
144144
neutral=neutral_element(op, typeof(init)),
145-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon}=nothing,
145+
dims=nothing,
146146
147147
# CPU settings
148148
max_tasks::Int=Threads.nthreads(),
@@ -158,7 +158,7 @@ end
158158
mapreduce(f, op, A::AbstractArray, B::AbstractArray, As::AbstractArray..., backend::Backend; init, kwargs...)
159159
160160
Reduce `src` along dimensions `dims` using the binary operator `op` after applying `f` elementwise.
161-
If `dims` is `nothing` or `:`, reduce `src` to a scalar. If `dims` is an integer or a tuple of
161+
If `dims` is `nothing` or `:`, reduce `src` to a scalar. If `dims` is an integer or a collection of
162162
integers, reduce `src` along those dimension(s). The `init` value is used as the initial value for
163163
the reduction (i.e. after mapping).
164164
@@ -175,16 +175,16 @@ are reduced without materializing the intermediate array. Mismatched axes throw
175175
176176
## CPU settings
177177
Use at most `max_tasks` threads with at least `min_elems` elements per task. For N-dimensional
178-
arrays (`dims` is an integer or tuple) multithreading currently only becomes faster for
179-
`max_tasks >= 4`; all other cases are scaling linearly with the number of threads.
178+
arrays (`dims` is an integer or a collection of integers) multithreading currently only becomes
179+
faster for `max_tasks >= 4`; all other cases are scaling linearly with the number of threads.
180180
181181
## GPU settings
182182
The `block_size` parameter controls the number of threads per block and must be a power of two.
183183
184184
The `temp` parameter can be used to pass a pre-allocated temporary array. For reduction to a scalar
185185
(`dims=nothing` or `dims=:`), `length(temp) >= 2 * (length(src) + 2 * block_size - 1) ÷ (2 *
186-
block_size)` is required. For reduction along dimensions (`dims` is an integer or tuple), `temp` is
187-
used as the destination array, and thus must have the exact dimensions required - i.e. same
186+
block_size)` is required. For reduction along dimensions (`dims` is an integer or a collection of
187+
integers), `temp` is used as the destination array, and thus must have the exact dimensions required - i.e. same
188188
dimensionwise sizes as `src`, except for the reduced dimension(s) which become 1; there are some
189189
corner cases when one dimension is zero, check against `Base.reduce` for CPU arrays for exact
190190
behavior.
@@ -281,7 +281,7 @@ function _mapreduce_impl(
281281
f, op, src::MapReduceSource, backend::Backend;
282282
init,
283283
neutral=neutral_element(op, typeof(init)),
284-
dims::Union{Nothing, Int, Tuple{Vararg{Int}}, Colon} = nothing,
284+
dims = nothing,
285285

286286
# CPU settings
287287
max_tasks::Int=Threads.nthreads(),

test/generic/reduce.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ end
259259
end
260260
end
261261

262+
# Base also accepts iterable dims such as vectors and ranges.
263+
for dims in ([1,2], [1,3], [2,3], [1,2,3], [2,1], [2,1,2], Int[], Any[1,2], Int32[1,2], 1:2)
264+
vh = rand(Int32(1):Int32(100), 3, 4, 5)
265+
v = array_from_host(vh)
266+
@test Array(AK.reduce(+, v; prefer_threads, init=Int32(0), dims)) ==
267+
sum(vh; init=Int32(0), dims)
268+
end
269+
270+
@test_throws ArgumentError AK.reduce(+, array_from_host(rand(Int32, 3, 4)); prefer_threads, init=Int32(0), dims=[1.0, 2.0])
271+
262272
# Tiled strided GPU path: contiguous kept dimensions, one strided reduce
263273
# dimension, and dst_size == reduce_size. The 3D case also exercises a
264274
# partial output tile.
@@ -650,6 +660,16 @@ end
650660
end
651661
end
652662

663+
# Base also accepts iterable dims such as vectors and ranges.
664+
for dims in ([1,2], [1,3], [2,3], [1,2,3], [2,1], [2,1,2], Int[], Any[1,2], Int32[1,2], 1:2)
665+
vh = rand(Int32(1):Int32(100), 3, 4, 5)
666+
v = array_from_host(vh)
667+
@test Array(AK.mapreduce(-, +, v; prefer_threads, init=Int32(0), dims)) ==
668+
mapreduce(-, +, vh; init=Int32(0), dims)
669+
end
670+
671+
@test_throws ArgumentError AK.mapreduce(-, +, array_from_host(rand(Int32, 3, 4)); prefer_threads, init=Int32(0), dims=[1.0, 2.0])
672+
653673
# Tiled strided GPU path coverage for mapreduce, including a 3D case with
654674
# a partial output tile.
655675
for (shape, dims) in (((512, 512), 2), ((20, 13, 260), 3))

0 commit comments

Comments
 (0)