Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorReductionSycl.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Mehdi Goli Codeplay Software Ltd.
5// Ralph Potter Codeplay Software Ltd.
6// Luke Iwanski Codeplay Software Ltd.
7// Contact: <eigen@codeplay.com>
8//
9// This Source Code Form is subject to the terms of the Mozilla
10// Public License v. 2.0. If a copy of the MPL was not distributed
11// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
12// SPDX-FileCopyrightText: The Eigen Authors
13// SPDX-License-Identifier: MPL-2.0
14
15/*****************************************************************
16 * TensorReductionSycl.h
17 *
18 * \brief:
19 * This is the specialization of the reduction operation. Two phase reduction approach
20 * is used since the GPU does not have Global Synchronization for global memory among
21 * different work-group/thread block. To solve the problem, we need to create two kernels
22 * to reduce the data, where the first kernel reduce the data locally and each local
23 * workgroup/thread-block save the input data into global memory. In the second phase (global reduction)
24 * one work-group uses one work-group/thread-block to reduces the intermediate data into one single element.
25 * Here is an NVIDIA presentation explaining the optimized two phase reduction algorithm on GPU:
26 * https://developer.download.nvidia.com/assets/cuda/files/reduction.pdf
27 *
28 *****************************************************************/
29
30#ifndef UNSUPPORTED_EIGEN_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
31#define UNSUPPORTED_EIGEN_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
32// IWYU pragma: private
33#include "./InternalHeaderCheck.h"
34
35namespace Eigen {
36namespace TensorSycl {
37namespace internal {
38
39template <typename Op, typename CoeffReturnType, typename Index, bool Vectorizable>
40struct OpDefiner {
41 typedef typename Vectorise<CoeffReturnType, Eigen::SyclDevice, Vectorizable>::PacketReturnType PacketReturnType;
42 typedef Op type;
43 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE type get_op(Op &op) { return op; }
44
45 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType finalise_op(const PacketReturnType &accumulator,
46 const Index &) {
47 return accumulator;
48 }
49};
50
51template <typename CoeffReturnType, typename Index>
52struct OpDefiner<Eigen::internal::MeanReducer<CoeffReturnType>, CoeffReturnType, Index, false> {
53 typedef Eigen::internal::SumReducer<CoeffReturnType> type;
54 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE type get_op(Eigen::internal::MeanReducer<CoeffReturnType> &) {
55 return type();
56 }
57
58 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType finalise_op(const CoeffReturnType &accumulator,
59 const Index &scale) {
60 ::Eigen::internal::scalar_quotient_op<CoeffReturnType> quotient_op;
61 return quotient_op(accumulator, CoeffReturnType(scale));
62 }
63};
64
65template <typename CoeffReturnType, typename Index>
66struct OpDefiner<Eigen::internal::MeanReducer<CoeffReturnType>, CoeffReturnType, Index, true> {
67 typedef typename Vectorise<CoeffReturnType, Eigen::SyclDevice, true>::PacketReturnType PacketReturnType;
68 typedef Eigen::internal::SumReducer<CoeffReturnType> type;
69 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE type get_op(Eigen::internal::MeanReducer<CoeffReturnType> &) {
70 return type();
71 }
72
73 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType finalise_op(const PacketReturnType &accumulator,
74 const Index &scale) {
75 return ::Eigen::internal::pdiv(accumulator, ::Eigen::internal::pset1<PacketReturnType>(CoeffReturnType(scale)));
76 }
77};
78
79template <typename CoeffReturnType, typename OpType, typename InputAccessor, typename OutputAccessor, typename Index,
80 Index local_range>
81struct SecondStepFullReducer {
82 typedef cl::sycl::accessor<CoeffReturnType, 1, cl::sycl::access::mode::read_write, cl::sycl::access::target::local>
83 LocalAccessor;
84 typedef OpDefiner<OpType, CoeffReturnType, Index, true> OpDef;
85 typedef typename OpDef::type Op;
86 LocalAccessor scratch;
87 InputAccessor aI;
88 OutputAccessor outAcc;
89 Op op;
90 SecondStepFullReducer(LocalAccessor scratch_, InputAccessor aI_, OutputAccessor outAcc_, OpType op_)
91 : scratch(scratch_), aI(aI_), outAcc(outAcc_), op(OpDef::get_op(op_)) {}
92
93 void operator()(cl::sycl::nd_item<1> itemID) const {
94 // Our empirical research shows that the best performance will be achieved
95 // when there is only one element per thread to reduce in the second step.
96 // in this step the second step reduction time is almost negligible.
97 // Hence, in the second step of reduction the input size is fixed to the
98 // local size, thus, there is only one element read per thread. The
99 // algorithm must be changed if the number of reduce per thread in the
100 // second step is greater than 1. Otherwise, the result will be wrong.
101 const Index localid = itemID.get_local_id(0);
102 auto aInPtr = aI + localid;
103 auto aOutPtr = outAcc;
104 CoeffReturnType *scratchptr = scratch.get_pointer();
105 CoeffReturnType accumulator = *aInPtr;
106
107 scratchptr[localid] = op.finalize(accumulator);
108 for (Index offset = itemID.get_local_range(0) / 2; offset > 0; offset /= 2) {
109 itemID.barrier(cl::sycl::access::fence_space::local_space);
110 if (localid < offset) {
111 op.reduce(scratchptr[localid + offset], &accumulator);
112 scratchptr[localid] = op.finalize(accumulator);
113 }
114 }
115 if (localid == 0) *aOutPtr = op.finalize(accumulator);
116 }
117};
118
119// Full reduction first phase. In this version the vectorization is true and the reduction accept
120// any generic reducerOp e.g( max, min, sum, mean, iamax, iamin, etc ).
121template <typename Evaluator, typename OpType, typename Evaluator::Index local_range>
122class FullReductionKernelFunctor {
123 public:
124 typedef typename Evaluator::CoeffReturnType CoeffReturnType;
125 typedef typename Evaluator::Index Index;
126 typedef OpDefiner<OpType, typename Evaluator::CoeffReturnType, Index,
127 (Evaluator::ReducerTraits::PacketAccess & Evaluator::InputPacketAccess)>
128 OpDef;
129
130 typedef typename OpDef::type Op;
131 typedef typename Evaluator::EvaluatorPointerType EvaluatorPointerType;
132 typedef typename Evaluator::PacketReturnType PacketReturnType;
133 typedef std::conditional_t<(Evaluator::ReducerTraits::PacketAccess & Evaluator::InputPacketAccess), PacketReturnType,
134 CoeffReturnType>
135 OutType;
136 typedef cl::sycl::accessor<OutType, 1, cl::sycl::access::mode::read_write, cl::sycl::access::target::local>
137 LocalAccessor;
138 LocalAccessor scratch;
139 Evaluator evaluator;
140 EvaluatorPointerType final_output;
141 Index rng;
142 Op op;
143
144 FullReductionKernelFunctor(LocalAccessor scratch_, Evaluator evaluator_, EvaluatorPointerType final_output_,
145 Index rng_, OpType op_)
146 : scratch(scratch_), evaluator(evaluator_), final_output(final_output_), rng(rng_), op(OpDef::get_op(op_)) {}
147
148 void operator()(cl::sycl::nd_item<1> itemID) const { compute_reduction(itemID); }
149
150 template <bool Vect = (Evaluator::ReducerTraits::PacketAccess & Evaluator::InputPacketAccess)>
151 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<Vect> compute_reduction(
152 const cl::sycl::nd_item<1> &itemID) const {
153 auto output_ptr = final_output;
154 Index VectorizedRange = (rng / Evaluator::PacketSize) * Evaluator::PacketSize;
155 Index globalid = itemID.get_global_id(0);
156 Index localid = itemID.get_local_id(0);
157 Index step = Evaluator::PacketSize * itemID.get_global_range(0);
158 Index start = Evaluator::PacketSize * globalid;
159 // vectorizable parts
160 PacketReturnType packetAccumulator = op.template initializePacket<PacketReturnType>();
161 for (Index i = start; i < VectorizedRange; i += step) {
162 op.template reducePacket<PacketReturnType>(evaluator.impl().template packet<Unaligned>(i), &packetAccumulator);
163 }
164 globalid += VectorizedRange;
165 // non vectorizable parts
166 for (Index i = globalid; i < rng; i += itemID.get_global_range(0)) {
167 op.template reducePacket<PacketReturnType>(
168 ::Eigen::TensorSycl::internal::PacketWrapper<PacketReturnType, Evaluator::PacketSize>::convert_to_packet_type(
169 evaluator.impl().coeff(i), op.initialize()),
170 &packetAccumulator);
171 }
172 scratch[localid] = packetAccumulator =
173 OpDef::finalise_op(op.template finalizePacket<PacketReturnType>(packetAccumulator), rng);
174 // reduction parts // Local size is always power of 2
175 EIGEN_UNROLL_LOOP
176 for (Index offset = local_range / 2; offset > 0; offset /= 2) {
177 itemID.barrier(cl::sycl::access::fence_space::local_space);
178 if (localid < offset) {
179 op.template reducePacket<PacketReturnType>(scratch[localid + offset], &packetAccumulator);
180 scratch[localid] = op.template finalizePacket<PacketReturnType>(packetAccumulator);
181 }
182 }
183 if (localid == 0) {
184 output_ptr[itemID.get_group(0)] =
185 op.finalizeBoth(op.initialize(), op.template finalizePacket<PacketReturnType>(packetAccumulator));
186 }
187 }
188
189 template <bool Vect = (Evaluator::ReducerTraits::PacketAccess & Evaluator::InputPacketAccess)>
190 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<!Vect> compute_reduction(
191 const cl::sycl::nd_item<1> &itemID) const {
192 auto output_ptr = final_output;
193 Index globalid = itemID.get_global_id(0);
194 Index localid = itemID.get_local_id(0);
195 // vectorizable parts
196 CoeffReturnType accumulator = op.initialize();
197 // non vectorizable parts
198 for (Index i = globalid; i < rng; i += itemID.get_global_range(0)) {
199 op.reduce(evaluator.impl().coeff(i), &accumulator);
200 }
201 scratch[localid] = accumulator = OpDef::finalise_op(op.finalize(accumulator), rng);
202
203 // reduction parts. the local size is always power of 2
204 EIGEN_UNROLL_LOOP
205 for (Index offset = local_range / 2; offset > 0; offset /= 2) {
206 itemID.barrier(cl::sycl::access::fence_space::local_space);
207 if (localid < offset) {
208 op.reduce(scratch[localid + offset], &accumulator);
209 scratch[localid] = op.finalize(accumulator);
210 }
211 }
212 if (localid == 0) {
213 output_ptr[itemID.get_group(0)] = op.finalize(accumulator);
214 }
215 }
216};
217
218template <typename Evaluator, typename OpType>
219class GenericNondeterministicReducer {
220 public:
221 typedef typename Evaluator::CoeffReturnType CoeffReturnType;
222 typedef typename Evaluator::EvaluatorPointerType EvaluatorPointerType;
223 typedef typename Evaluator::Index Index;
224 typedef OpDefiner<OpType, CoeffReturnType, Index, false> OpDef;
225 typedef typename OpDef::type Op;
226 template <typename Scratch>
227 GenericNondeterministicReducer(Scratch, Evaluator evaluator_, EvaluatorPointerType output_accessor_, OpType functor_,
228 Index range_, Index num_values_to_reduce_)
229 : evaluator(evaluator_),
230 output_accessor(output_accessor_),
231 functor(OpDef::get_op(functor_)),
232 range(range_),
233 num_values_to_reduce(num_values_to_reduce_) {}
234
235 void operator()(cl::sycl::nd_item<1> itemID) const {
236 // This is to bypass the stateful condition in Eigen meanReducer
237 Op non_const_functor;
238 std::memcpy(&non_const_functor, &functor, sizeof(Op));
239 auto output_accessor_ptr = output_accessor;
240 Index globalid = static_cast<Index>(itemID.get_global_linear_id());
241 if (globalid < range) {
242 CoeffReturnType accum = functor.initialize();
243 Eigen::internal::GenericDimReducer<Evaluator::NumReducedDims - 1, Evaluator, Op>::reduce(
244 evaluator, evaluator.firstInput(globalid), non_const_functor, &accum);
245 output_accessor_ptr[globalid] = OpDef::finalise_op(functor.finalize(accum), num_values_to_reduce);
246 }
247 }
248
249 private:
250 Evaluator evaluator;
251 EvaluatorPointerType output_accessor;
252 Op functor;
253 Index range;
254 Index num_values_to_reduce;
255};
256
257enum class reduction_dim { inner_most, outer_most };
258// Default partial reduction (preserve dimensions).
259template <typename Evaluator, typename OpType, typename PannelParameters, reduction_dim rt>
260struct PartialReductionKernel {
261 typedef typename Evaluator::CoeffReturnType CoeffReturnType;
262 typedef typename Evaluator::EvaluatorPointerType EvaluatorPointerType;
263 typedef typename Evaluator::Index Index;
264 typedef OpDefiner<OpType, CoeffReturnType, Index, false> OpDef;
265 typedef typename OpDef::type Op;
266 typedef cl::sycl::accessor<CoeffReturnType, 1, cl::sycl::access::mode::read_write, cl::sycl::access::target::local>
267 ScratchAcc;
268 ScratchAcc scratch;
269 Evaluator evaluator;
270 EvaluatorPointerType output_accessor;
271 Op op;
272 const Index preserve_elements_num_groups;
273 const Index reduce_elements_num_groups;
274 const Index num_coeffs_to_preserve;
275 const Index num_coeffs_to_reduce;
276
277 PartialReductionKernel(ScratchAcc scratch_, Evaluator evaluator_, EvaluatorPointerType output_accessor_, OpType op_,
278 const Index preserve_elements_num_groups_, const Index reduce_elements_num_groups_,
279 const Index num_coeffs_to_preserve_, const Index num_coeffs_to_reduce_)
280 : scratch(scratch_),
281 evaluator(evaluator_),
282 output_accessor(output_accessor_),
283 op(OpDef::get_op(op_)),
284 preserve_elements_num_groups(preserve_elements_num_groups_),
285 reduce_elements_num_groups(reduce_elements_num_groups_),
286 num_coeffs_to_preserve(num_coeffs_to_preserve_),
287 num_coeffs_to_reduce(num_coeffs_to_reduce_) {}
288
289 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void element_wise_reduce(Index globalRId, Index globalPId,
290 CoeffReturnType &accumulator) const {
291 if (globalPId >= num_coeffs_to_preserve) {
292 return;
293 }
294 Index global_offset = rt == reduction_dim::outer_most ? globalPId + (globalRId * num_coeffs_to_preserve)
295 : globalRId + (globalPId * num_coeffs_to_reduce);
296 Index localOffset = globalRId;
297
298 const Index per_thread_local_stride = PannelParameters::LocalThreadSizeR * reduce_elements_num_groups;
299 const Index per_thread_global_stride =
300 rt == reduction_dim::outer_most ? num_coeffs_to_preserve * per_thread_local_stride : per_thread_local_stride;
301 for (Index i = globalRId; i < num_coeffs_to_reduce; i += per_thread_local_stride) {
302 op.reduce(evaluator.impl().coeff(global_offset), &accumulator);
303 localOffset += per_thread_local_stride;
304 global_offset += per_thread_global_stride;
305 }
306 }
307 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void operator()(cl::sycl::nd_item<1> itemID) const {
308 const Index linearLocalThreadId = itemID.get_local_id(0);
309 Index pLocalThreadId = rt == reduction_dim::outer_most ? linearLocalThreadId % PannelParameters::LocalThreadSizeP
310 : linearLocalThreadId / PannelParameters::LocalThreadSizeR;
311 Index rLocalThreadId = rt == reduction_dim::outer_most ? linearLocalThreadId / PannelParameters::LocalThreadSizeP
312 : linearLocalThreadId % PannelParameters::LocalThreadSizeR;
313 const Index pGroupId = rt == reduction_dim::outer_most ? itemID.get_group(0) % preserve_elements_num_groups
314 : itemID.get_group(0) / reduce_elements_num_groups;
315 const Index rGroupId = rt == reduction_dim::outer_most ? itemID.get_group(0) / preserve_elements_num_groups
316 : itemID.get_group(0) % reduce_elements_num_groups;
317
318 Index globalPId = pGroupId * PannelParameters::LocalThreadSizeP + pLocalThreadId;
319 const Index globalRId = rGroupId * PannelParameters::LocalThreadSizeR + rLocalThreadId;
320 CoeffReturnType *scratchPtr = scratch.get_pointer();
321 auto outPtr = output_accessor + (reduce_elements_num_groups > 1 ? rGroupId * num_coeffs_to_preserve : 0);
322 CoeffReturnType accumulator = op.initialize();
323
324 element_wise_reduce(globalRId, globalPId, accumulator);
325
326 accumulator = OpDef::finalise_op(op.finalize(accumulator), num_coeffs_to_reduce);
327 scratchPtr[pLocalThreadId + rLocalThreadId * (PannelParameters::LocalThreadSizeP + PannelParameters::BC)] =
328 accumulator;
329 if (rt == reduction_dim::inner_most) {
330 pLocalThreadId = linearLocalThreadId % PannelParameters::LocalThreadSizeP;
331 rLocalThreadId = linearLocalThreadId / PannelParameters::LocalThreadSizeP;
332 globalPId = pGroupId * PannelParameters::LocalThreadSizeP + pLocalThreadId;
333 }
334
335 /* Apply the reduction operation between the current local
336 * id and the one on the other half of the vector. */
337 auto out_scratch_ptr =
338 scratchPtr + (pLocalThreadId + (rLocalThreadId * (PannelParameters::LocalThreadSizeP + PannelParameters::BC)));
339 itemID.barrier(cl::sycl::access::fence_space::local_space);
340 if (rt == reduction_dim::inner_most) {
341 accumulator = *out_scratch_ptr;
342 }
343 // The Local LocalThreadSizeR is always power of 2
344 EIGEN_UNROLL_LOOP
345 for (Index offset = PannelParameters::LocalThreadSizeR >> 1; offset > 0; offset >>= 1) {
346 if (rLocalThreadId < offset) {
347 op.reduce(out_scratch_ptr[(PannelParameters::LocalThreadSizeP + PannelParameters::BC) * offset], &accumulator);
348 // The result has already been divided for mean reducer in the
349 // previous reduction so no need to divide furthermore
350 *out_scratch_ptr = op.finalize(accumulator);
351 }
352 /* All threads collectively read from global memory into local.
353 * The barrier ensures all threads' IO is resolved before
354 * execution continues (strictly speaking, all threads within
355 * a single work-group - there is no co-ordination between
356 * work-groups, only work-items). */
357 itemID.barrier(cl::sycl::access::fence_space::local_space);
358 }
359
360 if (rLocalThreadId == 0 && globalPId < num_coeffs_to_preserve) {
361 outPtr[globalPId] = op.finalize(accumulator);
362 }
363 }
364};
365
366template <typename OutScalar, typename Index, typename InputAccessor, typename OutputAccessor, typename OpType>
367struct SecondStepPartialReduction {
368 typedef OpDefiner<OpType, OutScalar, Index, false> OpDef;
369 typedef typename OpDef::type Op;
370 typedef cl::sycl::accessor<OutScalar, 1, cl::sycl::access::mode::read_write, cl::sycl::access::target::local>
371 ScratchAccessor;
372 InputAccessor input_accessor;
373 OutputAccessor output_accessor;
374 Op op;
375 const Index num_coeffs_to_preserve;
376 const Index num_coeffs_to_reduce;
377
378 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE SecondStepPartialReduction(ScratchAccessor, InputAccessor input_accessor_,
379 OutputAccessor output_accessor_, OpType op_,
380 const Index num_coeffs_to_preserve_,
381 const Index num_coeffs_to_reduce_)
382 : input_accessor(input_accessor_),
383 output_accessor(output_accessor_),
384 op(OpDef::get_op(op_)),
385 num_coeffs_to_preserve(num_coeffs_to_preserve_),
386 num_coeffs_to_reduce(num_coeffs_to_reduce_) {}
387
388 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void operator()(cl::sycl::nd_item<1> itemID) const {
389 const Index globalId = itemID.get_global_id(0);
390
391 if (globalId >= num_coeffs_to_preserve) return;
392
393 auto in_ptr = input_accessor + globalId;
394
395 OutScalar accumulator = op.initialize();
396 // num_coeffs_to_reduce is not bigger than 256
397 for (Index i = 0; i < num_coeffs_to_reduce; i++) {
398 op.reduce(*in_ptr, &accumulator);
399 in_ptr += num_coeffs_to_preserve;
400 }
401 output_accessor[globalId] = op.finalize(accumulator);
402 }
403};
404
405template <typename Index, Index LTP, Index LTR, bool BC_>
406struct ReductionPannel {
407 static constexpr Index LocalThreadSizeP = LTP;
408 static constexpr Index LocalThreadSizeR = LTR;
409 static constexpr bool BC = BC_;
410};
411
412template <typename Self, typename Op, TensorSycl::internal::reduction_dim rt>
413struct PartialReducerLauncher {
414 typedef typename Self::EvaluatorPointerType EvaluatorPointerType;
415 typedef typename Self::CoeffReturnType CoeffReturnType;
416 typedef typename Self::Storage Storage;
417 typedef typename Self::Index Index;
418 typedef ReductionPannel<typename Self::Index, EIGEN_SYCL_LOCAL_THREAD_DIM0, EIGEN_SYCL_LOCAL_THREAD_DIM1, true>
419 PannelParameters;
420
421 typedef PartialReductionKernel<Self, Op, PannelParameters, rt> SyclReducerKerneType;
422
423 static bool run(const Self &self, const Op &reducer, const Eigen::SyclDevice &dev, EvaluatorPointerType output,
424 Index num_coeffs_to_reduce, Index num_coeffs_to_preserve) {
425 Index roundUpP = roundUp(num_coeffs_to_preserve, PannelParameters::LocalThreadSizeP);
426
427 // getPowerOfTwo makes sure local range is power of 2 and <=
428 // maxSyclThreadPerBlock this will help us to avoid extra check on the
429 // kernel
430 static_assert(!((PannelParameters::LocalThreadSizeP * PannelParameters::LocalThreadSizeR) &
431 (PannelParameters::LocalThreadSizeP * PannelParameters::LocalThreadSizeR - 1)),
432 "The Local thread size must be a power of 2 for the reduction "
433 "operation");
434
435 constexpr Index localRange = PannelParameters::LocalThreadSizeP * PannelParameters::LocalThreadSizeR;
436 // In this step, we force the code not to be more than 2-step reduction:
437 // Our empirical research shows that if each thread reduces at least 64
438 // elements individually, we get better performance. However, this can change
439 // on different platforms. In this step we force the code not to be
440 // more than 2-step reduction: Our empirical research shows that for inner_most
441 // dim reducer, it is better to have 8 group in a reduce dimension for sizes
442 // > 1024 to achieve the best performance.
443 const Index reductionPerThread = 64;
444 Index cu = dev.getPowerOfTwo(dev.getNumSyclMultiProcessors(), true);
445 const Index pNumGroups = roundUpP / PannelParameters::LocalThreadSizeP;
446 Index rGroups = (cu + pNumGroups - 1) / pNumGroups;
447 const Index rNumGroups = num_coeffs_to_reduce > reductionPerThread * localRange ? std::min(rGroups, localRange) : 1;
448 const Index globalRange = pNumGroups * rNumGroups * localRange;
449
450 constexpr Index scratchSize =
451 PannelParameters::LocalThreadSizeR * (PannelParameters::LocalThreadSizeP + PannelParameters::BC);
452 auto thread_range = cl::sycl::nd_range<1>(cl::sycl::range<1>(globalRange), cl::sycl::range<1>(localRange));
453 if (rNumGroups > 1) {
454 CoeffReturnType *temp_pointer = static_cast<CoeffReturnType *>(
455 dev.allocate_temp(num_coeffs_to_preserve * rNumGroups * sizeof(CoeffReturnType)));
456 EvaluatorPointerType temp_accessor = dev.get(temp_pointer);
457 dev.template unary_kernel_launcher<CoeffReturnType, SyclReducerKerneType>(
458 self, temp_accessor, thread_range, scratchSize, reducer, pNumGroups, rNumGroups, num_coeffs_to_preserve,
459 num_coeffs_to_reduce)
460 .wait();
461 typedef SecondStepPartialReduction<CoeffReturnType, Index, EvaluatorPointerType, EvaluatorPointerType, Op>
462 SecondStepPartialReductionKernel;
463 dev.template unary_kernel_launcher<CoeffReturnType, SecondStepPartialReductionKernel>(
464 temp_accessor, output,
465 cl::sycl::nd_range<1>(cl::sycl::range<1>(pNumGroups * localRange), cl::sycl::range<1>(localRange)),
466 Index(1), reducer, num_coeffs_to_preserve, rNumGroups)
467 .wait();
468 self.device().deallocate_temp(temp_pointer);
469 } else {
470 dev.template unary_kernel_launcher<CoeffReturnType, SyclReducerKerneType>(
471 self, output, thread_range, scratchSize, reducer, pNumGroups, rNumGroups, num_coeffs_to_preserve,
472 num_coeffs_to_reduce)
473 .wait();
474 }
475 return false;
476 }
477};
478} // namespace internal
479} // namespace TensorSycl
480
481namespace internal {
482
483template <typename Self, typename Op, bool Vectorizable>
484struct FullReducer<Self, Op, Eigen::SyclDevice, Vectorizable> {
485 typedef typename Self::CoeffReturnType CoeffReturnType;
486 typedef typename Self::EvaluatorPointerType EvaluatorPointerType;
487 static constexpr bool HasOptimizedImplementation = true;
488 static constexpr int PacketSize = Self::PacketAccess ? Self::PacketSize : 1;
489 static void run(const Self &self, Op &reducer, const Eigen::SyclDevice &dev, EvaluatorPointerType data) {
490 typedef std::conditional_t<Self::PacketAccess, typename Self::PacketReturnType, CoeffReturnType> OutType;
491 static_assert(!((EIGEN_SYCL_LOCAL_THREAD_DIM0 * EIGEN_SYCL_LOCAL_THREAD_DIM1) &
492 (EIGEN_SYCL_LOCAL_THREAD_DIM0 * EIGEN_SYCL_LOCAL_THREAD_DIM1 - 1)),
493 "The Local thread size must be a power of 2 for the reduction "
494 "operation");
495 constexpr Index local_range = EIGEN_SYCL_LOCAL_THREAD_DIM0 * EIGEN_SYCL_LOCAL_THREAD_DIM1;
496
497 typename Self::Index inputSize = self.impl().dimensions().TotalSize();
498 // In this step we force the code not to be more than 2-step reduction:
499 // Our empirical research shows that if each thread reduces at least 512
500 // elements individually, we get better performance.
501 const Index reductionPerThread = 2048;
502 Index reductionGroup = dev.getPowerOfTwo(
503 (inputSize + (reductionPerThread * local_range - 1)) / (reductionPerThread * local_range), true);
504 const Index num_work_group = std::min(reductionGroup, local_range);
505 const Index global_range = num_work_group * local_range;
506
507 auto thread_range = cl::sycl::nd_range<1>(cl::sycl::range<1>(global_range), cl::sycl::range<1>(local_range));
508 typedef TensorSycl::internal::FullReductionKernelFunctor<Self, Op, local_range> reduction_kernel_t;
509 if (num_work_group > 1) {
510 CoeffReturnType *temp_pointer =
511 static_cast<CoeffReturnType *>(dev.allocate_temp(num_work_group * sizeof(CoeffReturnType)));
512 typename Self::EvaluatorPointerType tmp_global_accessor = dev.get(temp_pointer);
513 dev.template unary_kernel_launcher<OutType, reduction_kernel_t>(self, tmp_global_accessor, thread_range,
514 local_range, inputSize, reducer)
515 .wait();
516 typedef TensorSycl::internal::SecondStepFullReducer<CoeffReturnType, Op, EvaluatorPointerType,
517 EvaluatorPointerType, Index, local_range>
518 GenericRKernel;
519 dev.template unary_kernel_launcher<CoeffReturnType, GenericRKernel>(
520 tmp_global_accessor, data,
521 cl::sycl::nd_range<1>(cl::sycl::range<1>(num_work_group), cl::sycl::range<1>(num_work_group)),
522 num_work_group, reducer)
523 .wait();
524 dev.deallocate_temp(temp_pointer);
525 } else {
526 dev.template unary_kernel_launcher<OutType, reduction_kernel_t>(self, data, thread_range, local_range, inputSize,
527 reducer)
528 .wait();
529 }
530 }
531};
532// vectorizable inner_most most dim preserver
533// col reduction
534template <typename Self, typename Op>
535struct OuterReducer<Self, Op, Eigen::SyclDevice> {
536 static constexpr bool HasOptimizedImplementation = true;
537
538 static bool run(const Self &self, const Op &reducer, const Eigen::SyclDevice &dev,
539 typename Self::EvaluatorPointerType output, typename Self::Index num_coeffs_to_reduce,
540 typename Self::Index num_coeffs_to_preserve) {
541 return ::Eigen::TensorSycl::internal::PartialReducerLauncher<
542 Self, Op, ::Eigen::TensorSycl::internal::reduction_dim::outer_most>::run(self, reducer, dev, output,
543 num_coeffs_to_reduce,
544 num_coeffs_to_preserve);
545 }
546};
547// row reduction
548template <typename Self, typename Op>
549struct InnerReducer<Self, Op, Eigen::SyclDevice> {
550 static constexpr bool HasOptimizedImplementation = true;
551
552 static bool run(const Self &self, const Op &reducer, const Eigen::SyclDevice &dev,
553 typename Self::EvaluatorPointerType output, typename Self::Index num_coeffs_to_reduce,
554 typename Self::Index num_coeffs_to_preserve) {
555 return ::Eigen::TensorSycl::internal::PartialReducerLauncher<
556 Self, Op, ::Eigen::TensorSycl::internal::reduction_dim::inner_most>::run(self, reducer, dev, output,
557 num_coeffs_to_reduce,
558 num_coeffs_to_preserve);
559 }
560};
561
562// ArgMax uses this kernel for partial reduction.
563// TODO(@mehdi.goli): Come up with a better kernel.
564// generic partial reduction
565template <typename Self, typename Op>
566struct GenericReducer<Self, Op, Eigen::SyclDevice> {
567 static constexpr bool HasOptimizedImplementation = false;
568 static bool run(const Self &self, const Op &reducer, const Eigen::SyclDevice &dev,
569 typename Self::EvaluatorPointerType output, typename Self::Index num_values_to_reduce,
570 typename Self::Index num_coeffs_to_preserve) {
571 typename Self::Index range, GRange, tileSize;
572 dev.parallel_for_setup(num_coeffs_to_preserve, tileSize, range, GRange);
573
574 dev.template unary_kernel_launcher<typename Self::CoeffReturnType,
575 TensorSycl::internal::GenericNondeterministicReducer<Self, Op>>(
576 self, output, cl::sycl::nd_range<1>(cl::sycl::range<1>(GRange), cl::sycl::range<1>(tileSize)), Index(1),
577 reducer, range, (num_values_to_reduce != 0) ? num_values_to_reduce : static_cast<Index>(1))
578 .wait();
579 return false;
580 }
581};
582
583} // namespace internal
584} // namespace Eigen
585
586#endif // UNSUPPORTED_EIGEN_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
Namespace containing all symbols from the Eigen library.