@@ -105,6 +105,32 @@ function partial_mapreduce_device(f, op, neutral, maxitems, Rreduce, Rother, R,
105105 return
106106end
107107
108+ # Coalesced reduction for when the contiguous leading dimension is NOT reduced (the reduced
109+ # axes are strided). One work-item per output slice (Rother element), grid-strided; each
110+ # serially reduces over Rreduce. Consecutive work-items map to consecutive output slices,
111+ # which are consecutive in memory, so global reads are coalesced across lanes — the access
112+ # pattern a `dims=1` reduction already uses. On the Aurora LTS stack the workgroup-per-slice
113+ # kernel above reads a *strided* reduced dimension non-coalesced, which silently corrupts
114+ # large reductions (e.g. `sum(A; dims=2)`); this path avoids that pattern entirely.
115+ function coalesced_mapreduce_device (f, op, neutral, Rreduce, Rother, R, As... )
116+ iother = (get_group_id () - 1 ) * get_local_size () + get_local_id ()
117+ gstride = get_num_groups () * get_local_size ()
118+ @inbounds while iother <= length (Rother)
119+ Iother = Rother[iother]
120+ Iout = CartesianIndex (Tuple (Iother)... , 1 )
121+ neut = neutral === nothing ? R[Iout] : neutral
122+ val = op (neut, neut)
123+ for ireduce in 1 : length (Rreduce)
124+ Ireduce = Rreduce[ireduce]
125+ J = max (Iother, Ireduce)
126+ val = op (val, f (_map_getindex (As, J)... ))
127+ end
128+ R[Iout] = val
129+ iother += gstride
130+ end
131+ return
132+ end
133+
108134# # COV_EXCL_STOP
109135
110136function GPUArrays. mapreducedim! (f:: F , op:: OP , R:: oneWrappedArray{T} ,
@@ -133,6 +159,20 @@ function GPUArrays.mapreducedim!(f::F, op::OP, R::oneWrappedArray{T},
133159 # but allows us to write a generalized kernel supporting partial reductions.
134160 R′ = reshape (R, (size (R)... , 1 ))
135161
162+ # Aurora LTS workaround: the workgroup-per-slice kernel below reads a *strided* reduced
163+ # dimension non-coalesced, which silently corrupts reductions on this stack (regardless of
164+ # output count — it depends on the reduction length, not the number of slices). Whenever
165+ # the contiguous leading dimension is NOT reduced (`size(Rreduce, 1) == 1`), use the
166+ # coalesced one-work-item-per-slice kernel, whose lanes read consecutive memory. Few-slice
167+ # reductions get less parallelism but stay correct; the common many-slice case is also fast.
168+ if size (Rreduce, 1 ) == 1
169+ items = clamp (length (Rother), 1 , 256 )
170+ groups = min (cld (length (Rother), items), 1024 )
171+ @oneapi items= items groups= groups coalesced_mapreduce_device (
172+ f, op, init, Rreduce, Rother, R′, A)
173+ return R
174+ end
175+
136176 # how many items do we want?
137177 #
138178 # items in a group work together to reduce values across the reduction dimensions;
0 commit comments