11#ifndef EIGEN_TENSOR_TENSOR_REDUCTION_GPU_H
12#define EIGEN_TENSOR_TENSOR_REDUCTION_GPU_H
15#include "./InternalHeaderCheck.h"
20#if defined(EIGEN_USE_GPU) && defined(EIGEN_GPUCC)
27template <
typename T,
typename R>
28__device__ EIGEN_ALWAYS_INLINE
void atomicReduce(T* output, T accum, R& reducer) {
29 EIGEN_IF_CONSTEXPR (
sizeof(T) == 4) {
30 unsigned int oldval = *
reinterpret_cast<unsigned int*
>(output);
31 unsigned int newval = oldval;
32 reducer.reduce(accum,
reinterpret_cast<T*
>(&newval));
33 if (newval == oldval) {
36 unsigned int readback;
37 while ((readback = atomicCAS((
unsigned int*)output, oldval, newval)) != oldval) {
40 reducer.reduce(accum,
reinterpret_cast<T*
>(&newval));
41 if (newval == oldval) {
45 }
else EIGEN_IF_CONSTEXPR (
sizeof(T) == 8) {
46 unsigned long long oldval = *
reinterpret_cast<unsigned long long*
>(output);
47 unsigned long long newval = oldval;
48 reducer.reduce(accum,
reinterpret_cast<T*
>(&newval));
49 if (newval == oldval) {
52 unsigned long long readback;
53 while ((readback = atomicCAS(
reinterpret_cast<unsigned long long*
>(output), oldval, newval)) != oldval) {
56 reducer.reduce(accum,
reinterpret_cast<T*
>(&newval));
57 if (newval == oldval) {
62 gpu_assert(0 &&
"Wordsize not supported");
67template <
typename Type>
68__device__
inline Type atomicExchCustom(Type* address, Type val) {
69 return atomicExch(address, val);
73EIGEN_DEVICE_FUNC EIGEN_CONSTEXPR
auto reduction_shuffle_mask() {
74#if defined(EIGEN_HIP_DEVICE_COMPILE)
75 return 0xFFFFFFFFFFFFFFFFull;
82__device__ EIGEN_ALWAYS_INLINE T reduction_shuffle_down(T value,
int offset) {
83#if defined(EIGEN_HIPCC)
84 return __shfl_down(value, offset, warpSize);
86 return __shfl_down_sync(reduction_shuffle_mask<T>(), value, offset, warpSize);
91__device__ EIGEN_ALWAYS_INLINE
int reduction_shuffle_down<int>(
int value,
int offset) {
92#if defined(EIGEN_HIPCC)
93 return __shfl_down(value, offset, warpSize);
95 return __shfl_down_sync(reduction_shuffle_mask<int>(), value, offset, warpSize);
100__device__ EIGEN_ALWAYS_INLINE
float reduction_shuffle_down<float>(
float value,
int offset) {
101#if defined(EIGEN_HIPCC)
102 return __shfl_down(value, offset, warpSize);
104 return __shfl_down_sync(reduction_shuffle_mask<float>(), value, offset, warpSize);
109__device__ EIGEN_ALWAYS_INLINE
double reduction_shuffle_down<double>(
double value,
int offset) {
110#if defined(EIGEN_HIPCC)
111 return __shfl_down(value, offset, warpSize);
113 return __shfl_down_sync(reduction_shuffle_mask<double>(), value, offset, warpSize);
118__device__
inline double atomicExchCustom(
double* address,
double val) {
119 unsigned long long int* address_as_ull =
reinterpret_cast<unsigned long long int*
>(address);
120 return __longlong_as_double(atomicExch(address_as_ull, __double_as_longlong(val)));
125__device__
inline void atomicReduce(half2* output, half2 accum, R& reducer) {
126 unsigned int oldval = *
reinterpret_cast<unsigned int*
>(output);
127 unsigned int newval = oldval;
128 reducer.reducePacket(accum,
reinterpret_cast<half2*
>(&newval));
129 if (newval == oldval) {
132 unsigned int readback;
133 while ((readback = atomicCAS((
unsigned int*)output, oldval, newval)) != oldval) {
136 reducer.reducePacket(accum,
reinterpret_cast<half2*
>(&newval));
137 if (newval == oldval) {
142#ifdef EIGEN_GPU_COMPILE_PHASE
145__device__
inline void atomicReduce(Packet4h2* output, Packet4h2 accum, R& reducer) {
146 half2* houtput =
reinterpret_cast<half2*
>(output);
147 half2* haccum =
reinterpret_cast<half2*
>(&accum);
148 for (
int i = 0; i < 4; ++i) {
149 atomicReduce(houtput + i, *(haccum + i), reducer);
155__device__
inline void atomicReduce(
float* output,
float accum, SumReducer<float>&) {
156 atomicAdd(output, accum);
160__device__
inline void atomicReduce(
double* output,
double accum, SumReducer<double>&) {
161 atomicAdd(output, accum);
164template <
typename CoeffType,
typename Index>
165__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void ReductionInitKernel(
const CoeffType val, Index num_preserved_coeffs,
167 const Index thread_id = blockIdx.x * blockDim.x + threadIdx.x;
168 const Index num_threads = blockDim.x * gridDim.x;
169 for (Index i = thread_id; i < num_preserved_coeffs; i += num_threads) {
174template <
int BlockSize,
int NumPerThread,
typename Self,
typename Reducer,
typename Index>
175__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void FullReductionKernel(Reducer reducer,
const Self input, Index num_coeffs,
176 typename Self::CoeffReturnType* output,
177 unsigned int* semaphore) {
179 const Index first_index = blockIdx.x * BlockSize * NumPerThread + threadIdx.x;
180 if (gridDim.x == 1) {
181 if (first_index == 0) {
182 *output = reducer.initialize();
185 if (threadIdx.x == 0) {
186 unsigned int block = atomicCAS(semaphore, 0u, 1u);
189 atomicExchCustom(output, reducer.initialize());
191 atomicExch(semaphore, 2u);
197 val = atomicCAS(semaphore, 2u, 2u);
205 eigen_assert(gridDim.x == 1 || *semaphore >= 2u);
207 typename Self::CoeffReturnType accum = reducer.initialize();
208 Index max_iter = numext::mini<Index>(num_coeffs - first_index, NumPerThread * BlockSize);
209 for (Index i = 0; i < max_iter; i += BlockSize) {
210 const Index index = first_index + i;
211 eigen_assert(index < num_coeffs);
212 typename Self::CoeffReturnType val = input.m_impl.coeff(index);
213 reducer.reduce(val, &accum);
217 for (
int offset = warpSize / 2; offset > 0; offset /= 2) {
218 reducer.reduce(reduction_shuffle_down(accum, offset), &accum);
221 if ((threadIdx.x & (warpSize - 1)) == 0) {
222 atomicReduce(output, accum, reducer);
225 if (gridDim.x > 1 && threadIdx.x == 0) {
227 atomicInc(semaphore, gridDim.x + 1);
228#if defined(EIGEN_HIPCC)
229 __threadfence_system();
235template <
typename Self,
typename Reducer,
typename Index>
236__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void ReductionInitFullReduxKernelHalfFloat(Reducer reducer,
const Self input,
237 Index num_coeffs, half* scratch) {
238 eigen_assert(blockDim.x == 1);
239 eigen_assert(gridDim.x == 1);
240 typedef packet_traits<Eigen::half>::type packet_type;
241 Index packet_remainder = num_coeffs % Index(unpacket_traits<packet_type>::size);
242 if (packet_remainder != 0) {
243 half2* h2scratch =
reinterpret_cast<half2*
>(scratch);
244 for (Index i = num_coeffs - packet_remainder; i + 2 <= num_coeffs; i += 2) {
245 *h2scratch = __halves2half2(input.coeff(i), input.coeff(i + 1));
248 if ((num_coeffs & 1) != 0) {
249 half lastCoeff = input.coeff(num_coeffs - 1);
250 *h2scratch = __halves2half2(lastCoeff, reducer.initialize());
253 packet_type reduce = reducer.template initializePacket<packet_type>();
254 internal::pstoreu(scratch, reduce);
258template <
typename Self,
typename Reducer,
typename Index>
259__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void ReductionInitKernelHalfFloat(Reducer reducer,
const Self ,
260 Index num_coeffs, half* output) {
261 const Index thread_id = blockIdx.x * blockDim.x + threadIdx.x;
262 const Index num_threads = blockDim.x * gridDim.x;
263 typedef typename packet_traits<Eigen::half>::type PacketType;
265 const Index num_packets = num_coeffs / Index(unpacket_traits<PacketType>::size);
266 PacketType* p_output =
reinterpret_cast<PacketType*
>(output);
267 for (Index i = thread_id; i < num_packets; i += num_threads) {
268 p_output[i] = reducer.template initializePacket<PacketType>();
270 Index packet_remainder = num_coeffs % Index(unpacket_traits<PacketType>::size);
271 if (thread_id < packet_remainder) {
272 output[num_coeffs - packet_remainder + thread_id] = reducer.initialize();
276template <
int BlockSize,
int NumPerThread,
typename Self,
typename Reducer,
typename Index>
277__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void FullReductionKernelHalfFloat(Reducer reducer,
const Self input,
278 Index num_coeffs, half* output,
280 typedef typename packet_traits<Eigen::half>::type PacketType;
281 const int packet_width = unpacket_traits<PacketType>::size;
282 eigen_assert(NumPerThread % packet_width == 0);
283 const Index first_index = blockIdx.x * BlockSize * NumPerThread + packet_width * threadIdx.x;
287 if (gridDim.x == 1) {
288 if (first_index == 0) {
289 int rem = num_coeffs % packet_width;
291 half2* p_scratch =
reinterpret_cast<half2*
>(scratch);
292 pstoreu(scratch, reducer.template initializePacket<PacketType>());
293 for (
int i = 0; i < rem / 2; i++) {
294 *p_scratch = __halves2half2(input.coeff(num_coeffs - packet_width + 2 * i),
295 input.coeff(num_coeffs - packet_width + 2 * i + 1));
298 if ((num_coeffs & 1) != 0) {
299 half last = input.coeff(num_coeffs - 1);
300 *p_scratch = __halves2half2(last, reducer.initialize());
303 PacketType reduce = reducer.template initializePacket<PacketType>();
304 pstoreu(scratch, reduce);
310 PacketType accum = reducer.template initializePacket<PacketType>();
311 const Index max_iter =
312 numext::mini<Index>((num_coeffs - first_index) / packet_width, NumPerThread * BlockSize / packet_width);
313 for (Index i = 0; i < max_iter; i += BlockSize) {
314 const Index index = first_index + packet_width * i;
315 eigen_assert(index + packet_width < num_coeffs);
316 PacketType val = input.template packet<Unaligned>(index);
317 reducer.reducePacket(val, &accum);
321 for (
int offset = warpSize / 2; offset > 0; offset /= 2) {
322#if defined(EIGEN_HIPCC)
324 half2* hr =
reinterpret_cast<half2*
>(&r1);
325 half2* hacc =
reinterpret_cast<half2*
>(&accum);
326 for (
int i = 0; i < packet_width / 2; i++) {
333 wka_out.i = __shfl_down(wka_in.i, offset, warpSize);
336 reducer.reducePacket(r1, &accum);
339 half2* hr =
reinterpret_cast<half2*
>(&r1);
340 half2* hacc =
reinterpret_cast<half2*
>(&accum);
341 for (
int i = 0; i < packet_width / 2; i++) {
342 hr[i] = __shfl_down_sync(0xFFFFFFFF, hacc[i], (
unsigned)offset, warpSize);
344 reducer.reducePacket(r1, &accum);
349 if ((threadIdx.x & (warpSize - 1)) == 0) {
350 atomicReduce(
reinterpret_cast<PacketType*
>(scratch), accum, reducer);
354 half2* rv1 =
reinterpret_cast<half2*
>(scratch);
355 if (packet_width > 2) {
356 reducer.reducePacket(rv1[2], rv1);
357 reducer.reducePacket(rv1[3], rv1 + 1);
358 reducer.reducePacket(rv1[1], rv1);
360 if (gridDim.x == 1) {
361 if (first_index == 0) {
362 half tmp = __low2half(*rv1);
363 reducer.reduce(__high2half(*rv1), &tmp);
369template <
typename Op>
370__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void ReductionCleanupKernelHalfFloat(Op reducer, half* output, half* scratch) {
371 eigen_assert(threadIdx.x == 1);
372 typedef packet_traits<Eigen::half>::type packet_type;
373 EIGEN_IF_CONSTEXPR (unpacket_traits<packet_type>::size == 1) {
376 half2* pscratch =
reinterpret_cast<half2*
>(scratch);
377 half tmp = __float2half(0.f);
378 for (
int i = 0; i < unpacket_traits<packet_type>::size; i += 2) {
379 reducer.reduce(__low2half(*pscratch), &tmp);
380 reducer.reduce(__high2half(*pscratch), &tmp);
387template <
typename Self,
typename Op,
typename OutputType,
bool PacketAccess,
typename Enabled =
void>
388struct FullReductionLauncher {
389 static void run(
const Self&, Op&,
const GpuDevice&, OutputType*,
typename Self::Index) {
390 gpu_assert(
false &&
"Should only be called on doubles, floats and half floats");
395template <
typename Self,
typename Op,
typename OutputType,
bool PacketAccess>
396struct FullReductionLauncher<
397 Self, Op, OutputType, PacketAccess,
398 std::enable_if_t<std::is_same<float, OutputType>::value || std::is_same<double, OutputType>::value, void>> {
399 static void run(
const Self& self, Op& reducer,
const GpuDevice& device, OutputType* output,
400 typename Self::Index num_coeffs) {
401 typedef typename Self::Index Index;
402 const int block_size = 256;
403 const int num_per_thread = 128;
404 const int num_blocks = numext::div_ceil<int>(num_coeffs, block_size * num_per_thread);
406 unsigned int* semaphore =
nullptr;
407 if (num_blocks > 1) {
408 semaphore = device.semaphore();
411 LAUNCH_GPU_KERNEL((FullReductionKernel<block_size, num_per_thread, Self, Op, Index>), num_blocks, block_size, 0,
412 device, reducer, self, num_coeffs, output, semaphore);
417template <
typename Self,
typename Op>
418struct FullReductionLauncher<Self, Op, Eigen::half, false> {
419 static void run(
const Self&, Op&,
const GpuDevice&, half*,
typename Self::Index) {
420 gpu_assert(
false &&
"Should not be called since there is no packet accessor");
424template <
typename Self,
typename Op>
425struct FullReductionLauncher<Self, Op, Eigen::half, true> {
426 static void run(
const Self& self, Op& reducer,
const GpuDevice& device, half* output,
427 typename Self::Index num_coeffs) {
428 typedef typename Self::Index Index;
430 const int block_size = 256;
431 const int num_per_thread = 128;
432 const int num_blocks = numext::div_ceil<int>(num_coeffs, block_size * num_per_thread);
433 half* scratch =
static_cast<half*
>(device.scratchpad());
435 if (num_blocks > 1) {
438 LAUNCH_GPU_KERNEL((ReductionInitFullReduxKernelHalfFloat<Self, Op, Index>), 1, 1, 0, device, reducer, self,
439 num_coeffs, scratch);
442 LAUNCH_GPU_KERNEL((FullReductionKernelHalfFloat<block_size, num_per_thread, Self, Op, Index>), num_blocks,
443 block_size, 0, device, reducer, self, num_coeffs, output, scratch);
445 if (num_blocks > 1) {
446 LAUNCH_GPU_KERNEL((ReductionCleanupKernelHalfFloat<Op>), 1, 1, 0, device, reducer, output, scratch);
451template <
typename Self,
typename Op,
bool Vectorizable>
452struct FullReducer<Self, Op, GpuDevice, Vectorizable> {
457 static constexpr bool HasOptimizedImplementation =
458 !Self::ReducerTraits::IsStateful && (std::is_same<typename Self::CoeffReturnType, float>::value ||
459 std::is_same<typename Self::CoeffReturnType, double>::value ||
460 (std::is_same<typename Self::CoeffReturnType, Eigen::half>::value &&
461 reducer_traits<Op, GpuDevice>::PacketAccess));
463 template <
typename OutputType>
464 static void run(
const Self& self, Op& reducer,
const GpuDevice& device, OutputType* output) {
465 gpu_assert(HasOptimizedImplementation &&
"Should only be called on doubles, floats or half floats");
466 const Index num_coeffs = array_prod(self.m_impl.dimensions());
468 if (num_coeffs == 0) {
472 FullReductionLauncher<Self, Op, OutputType, reducer_traits<Op, GpuDevice>::PacketAccess>::run(self, reducer, device,
477template <
int NumPerThread,
typename Self,
typename Reducer,
typename Index>
478__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void InnerReductionKernel(Reducer reducer,
const Self input,
479 Index num_coeffs_to_reduce,
480 Index num_preserved_coeffs,
481 typename Self::CoeffReturnType* output) {
482 typedef typename Self::CoeffReturnType Type;
483 eigen_assert(blockDim.y == 1);
484 eigen_assert(blockDim.z == 1);
485 eigen_assert(gridDim.y == 1);
486 eigen_assert(gridDim.z == 1);
488 const int unroll_times = 16;
489 eigen_assert(NumPerThread % unroll_times == 0);
491 const Index input_col_blocks = numext::div_ceil<Index>(num_coeffs_to_reduce, blockDim.x * NumPerThread);
492 const Index num_input_blocks = input_col_blocks * num_preserved_coeffs;
494 const Index num_threads = blockDim.x * gridDim.x;
495 const Index thread_id = blockIdx.x * blockDim.x + threadIdx.x;
498 if (gridDim.x == 1) {
499 for (Index i = thread_id; i < num_preserved_coeffs; i += num_threads) {
500 output[i] = reducer.initialize();
505 for (Index i = blockIdx.x; i < num_input_blocks; i += gridDim.x) {
506 const Index row = i / input_col_blocks;
510 if (row < num_preserved_coeffs) {
511 const Index col_block = i % input_col_blocks;
512 const Index col_begin = col_block * blockDim.x * NumPerThread + threadIdx.x;
514 Type reduced_val = reducer.initialize();
516 for (Index j = 0; j < NumPerThread; j += unroll_times) {
517 const Index last_col = col_begin + blockDim.x * (j + unroll_times - 1);
518 if (last_col >= num_coeffs_to_reduce) {
519 for (Index col = col_begin + blockDim.x * j; col < num_coeffs_to_reduce; col += blockDim.x) {
520 const Type val = input.m_impl.coeff(row * num_coeffs_to_reduce + col);
521 reducer.reduce(val, &reduced_val);
527 for (
int k = 0; k < unroll_times; ++k) {
528 const Index col = col_begin + blockDim.x * (j + k);
529 reducer.reduce(input.m_impl.coeff(row * num_coeffs_to_reduce + col), &reduced_val);
535 for (
int offset = warpSize / 2; offset > 0; offset /= 2) {
536 reducer.reduce(reduction_shuffle_down(reduced_val, offset), &reduced_val);
539 if ((threadIdx.x & (warpSize - 1)) == 0) {
540 atomicReduce(&(output[row]), reduced_val, reducer);
548template <
int NumPerThread,
typename Self,
typename Reducer,
typename Index>
549__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void InnerReductionKernelHalfFloat(Reducer reducer,
const Self input,
550 Index num_coeffs_to_reduce,
551 Index num_preserved_coeffs, half* output) {
552 eigen_assert(blockDim.y == 1);
553 eigen_assert(blockDim.z == 1);
554 eigen_assert(gridDim.y == 1);
555 eigen_assert(gridDim.z == 1);
557 typedef typename packet_traits<Eigen::half>::type PacketType;
558 const int packet_width = unpacket_traits<PacketType>::size;
559 const int unroll_times = 16 / packet_width;
560 eigen_assert(NumPerThread % unroll_times == 0);
561 eigen_assert(unroll_times % 2 == 0);
563 const Index input_col_blocks = numext::div_ceil<Index>(num_coeffs_to_reduce, blockDim.x * NumPerThread * 2);
564 const Index num_input_blocks = numext::div_ceil<Index>(input_col_blocks * num_preserved_coeffs, 2);
566 const Index num_threads = blockDim.x * gridDim.x;
567 const Index thread_id = blockIdx.x * blockDim.x + threadIdx.x;
570 if (gridDim.x == 1) {
571 Index i = packet_width * thread_id;
572 for (; i + packet_width <= num_preserved_coeffs; i += packet_width * num_threads) {
573 PacketType* poutput =
reinterpret_cast<PacketType*
>(output + i);
574 *poutput = reducer.template initializePacket<PacketType>();
576 if (i < num_preserved_coeffs) {
577 output[i] = reducer.initialize();
582 for (Index i = blockIdx.x; i < num_input_blocks; i += gridDim.x) {
583 const Index row = 2 * (i / input_col_blocks);
586 if (row + 1 < num_preserved_coeffs) {
587 const Index col_block = i % input_col_blocks;
588 const Index col_begin = packet_width * (col_block * blockDim.x * NumPerThread + threadIdx.x);
590 PacketType reduced_val1 = reducer.template initializePacket<PacketType>();
591 PacketType reduced_val2 = reducer.template initializePacket<PacketType>();
593 for (Index j = 0; j < NumPerThread; j += unroll_times) {
594 const Index last_col = col_begin + blockDim.x * (j + unroll_times - 1) * packet_width;
595 if (last_col >= num_coeffs_to_reduce) {
596 Index col = col_begin + blockDim.x * j;
597 for (; col + packet_width <= num_coeffs_to_reduce; col += blockDim.x) {
598 const PacketType val1 = input.m_impl.template packet<Unaligned>(row * num_coeffs_to_reduce + col);
599 reducer.reducePacket(val1, &reduced_val1);
600 const PacketType val2 = input.m_impl.template packet<Unaligned>((row + 1) * num_coeffs_to_reduce + col);
601 reducer.reducePacket(val2, &reduced_val2);
603 if (col < num_coeffs_to_reduce) {
604 PacketType r1 = reducer.template initializePacket<PacketType>();
605 PacketType r2 = reducer.template initializePacket<PacketType>();
606 half2* hr1 =
reinterpret_cast<half2*
>(&r1);
607 half2* hr2 =
reinterpret_cast<half2*
>(&r2);
608 while (col + 1 < num_coeffs_to_reduce) {
609 *hr1 = __halves2half2(input.m_impl.coeff(row * num_coeffs_to_reduce + col),
610 input.m_impl.coeff(row * num_coeffs_to_reduce + col + 1));
611 *hr2 = __halves2half2(input.m_impl.coeff((row + 1) * num_coeffs_to_reduce + col),
612 input.m_impl.coeff((row + 1) * num_coeffs_to_reduce + col + 1));
617 if (col < num_coeffs_to_reduce) {
619 const half last1 = input.m_impl.coeff(row * num_coeffs_to_reduce + col);
620 *hr1 = __halves2half2(last1, reducer.initialize());
621 const half last2 = input.m_impl.coeff((row + 1) * num_coeffs_to_reduce + col);
622 *hr2 = __halves2half2(last2, reducer.initialize());
624 reducer.reducePacket(r1, &reduced_val1);
625 reducer.reducePacket(r2, &reduced_val2);
631 for (
int k = 0; k < unroll_times; ++k) {
632 const Index col = col_begin + blockDim.x * (j + k) * packet_width;
633 reducer.reducePacket(input.m_impl.template packet<Unaligned>(row * num_coeffs_to_reduce + col),
635 reducer.reducePacket(input.m_impl.template packet<Unaligned>((row + 1) * num_coeffs_to_reduce + col),
642 for (
int offset = warpSize / 2; offset > 0; offset /= 2) {
643#if defined(EIGEN_HIPCC)
646 half2* hr1 =
reinterpret_cast<half2*
>(&r1);
647 half2* hr2 =
reinterpret_cast<half2*
>(&r2);
648 half2* rv1 =
reinterpret_cast<half2*
>(&reduced_val1);
649 half2* rv2 =
reinterpret_cast<half2*
>(&reduced_val2);
650 for (
int i = 0; i < packet_width / 2; i++) {
657 wka_out1.i = __shfl_down(wka_in1.i, offset, warpSize);
665 wka_out2.i = __shfl_down(wka_in2.i, offset, warpSize);
668 reducer.reducePacket(r1, &reduced_val1);
669 reducer.reducePacket(r2, &reduced_val2);
673 half2* hr1 =
reinterpret_cast<half2*
>(&r1);
674 half2* hr2 =
reinterpret_cast<half2*
>(&r2);
675 half2* rr1 =
reinterpret_cast<half2*
>(&reduced_val1);
676 half2* rr2 =
reinterpret_cast<half2*
>(&reduced_val2);
677 for (
int j = 0; j < packet_width / 2; j++) {
678 hr1[j] = __shfl_down_sync(0xFFFFFFFF, rr1[j], (
unsigned)offset, warpSize);
679 hr2[j] = __shfl_down_sync(0xFFFFFFFF, rr2[j], (
unsigned)offset, warpSize);
681 reducer.reducePacket(r1, &reduced_val1);
682 reducer.reducePacket(r2, &reduced_val2);
686 half2* rv1 =
reinterpret_cast<half2*
>(&reduced_val1);
687 half2* rv2 =
reinterpret_cast<half2*
>(&reduced_val2);
689 if (packet_width > 2) {
690 reducer.reducePacket(rv1[2], rv1);
691 reducer.reducePacket(rv1[3], rv1 + 1);
692 reducer.reducePacket(rv1[1], rv1);
693 reducer.reducePacket(rv2[2], rv2);
694 reducer.reducePacket(rv2[3], rv2 + 1);
695 reducer.reducePacket(rv2[1], rv2);
697 half val1 = __low2half(*rv1);
698 reducer.reduce(__high2half(*rv1), &val1);
699 half val2 = __low2half(*rv2);
700 reducer.reduce(__high2half(*rv2), &val2);
701 val = __halves2half2(val1, val2);
702 if ((threadIdx.x & (warpSize - 1)) == 0) {
703 half* loc = output + row;
704 atomicReduce(
reinterpret_cast<half2*
>(loc), val, reducer);
710template <
typename Self,
typename Op,
typename OutputType,
bool PacketAccess,
typename Enabled =
void>
711struct InnerReductionLauncher {
712 static EIGEN_DEVICE_FUNC
bool run(
const Self&, Op&,
const GpuDevice&, OutputType*,
typename Self::Index,
713 typename Self::Index) {
714 gpu_assert(
false &&
"Should only be called to reduce doubles, floats and half floats on a gpu device");
720template <
typename Self,
typename Op,
typename OutputType,
bool PacketAccess>
721struct InnerReductionLauncher<
722 Self, Op, OutputType, PacketAccess,
723 std::enable_if_t<std::is_same<float, OutputType>::value || std::is_same<double, OutputType>::value, void>> {
724 static bool run(
const Self& self, Op& reducer,
const GpuDevice& device, OutputType* output,
725 typename Self::Index num_coeffs_to_reduce,
typename Self::Index num_preserved_vals) {
726 typedef typename Self::Index Index;
728 const Index num_coeffs = num_coeffs_to_reduce * num_preserved_vals;
729 const int block_size = 256;
730 const int num_per_thread = 128;
731 const int dyn_blocks = numext::div_ceil<int>(num_coeffs, block_size * num_per_thread);
732 const int max_blocks = device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor() / block_size;
733 const int num_blocks = numext::mini<int>(max_blocks, dyn_blocks);
735 if (num_blocks > 1) {
738 const int dyn_blocks2 = numext::div_ceil<int>(num_preserved_vals, 1024);
739 const int max_blocks2 = device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor() / 1024;
740 const int num_blocks2 = numext::mini<int>(max_blocks2, dyn_blocks2);
741 LAUNCH_GPU_KERNEL((ReductionInitKernel<OutputType, Index>), num_blocks2, 1024, 0, device, reducer.initialize(),
742 num_preserved_vals, output);
745 LAUNCH_GPU_KERNEL((InnerReductionKernel<num_per_thread, Self, Op, Index>), num_blocks, block_size, 0, device,
746 reducer, self, num_coeffs_to_reduce, num_preserved_vals, output);
753template <
typename Self,
typename Op>
754struct InnerReductionLauncher<Self, Op, Eigen::half, false> {
755 static bool run(
const Self&, Op&,
const GpuDevice&, half*,
typename Self::Index,
typename Self::Index) {
756 gpu_assert(
false &&
"Should not be called since there is no packet accessor");
761template <
typename Self,
typename Op>
762struct InnerReductionLauncher<Self, Op, Eigen::half, true> {
763 static bool run(
const Self& self, Op& reducer,
const GpuDevice& device, half* output,
764 typename Self::Index num_coeffs_to_reduce,
typename Self::Index num_preserved_vals) {
765 typedef typename Self::Index Index;
767 if (num_preserved_vals % 2 != 0) {
772 const Index num_coeffs = num_coeffs_to_reduce * num_preserved_vals;
773 const int block_size = 128;
774 const int num_per_thread = 64;
775 const int dyn_blocks = numext::div_ceil<int>(num_coeffs, block_size * num_per_thread);
776 const int max_blocks = device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor() / block_size;
777 const int num_blocks = numext::mini<int>(max_blocks, dyn_blocks);
779 if (num_blocks > 1) {
782 LAUNCH_GPU_KERNEL((ReductionInitKernelHalfFloat<Self, Op, Index>), 1, 1, 0, device, reducer, self,
783 num_preserved_vals, output);
786 LAUNCH_GPU_KERNEL((InnerReductionKernelHalfFloat<num_per_thread, Self, Op, Index>), num_blocks, block_size, 0,
787 device, reducer, self, num_coeffs_to_reduce, num_preserved_vals, output);
793template <
typename Self,
typename Op>
794struct InnerReducer<Self, Op, GpuDevice> {
799 static constexpr bool HasOptimizedImplementation =
800 !Self::ReducerTraits::IsStateful && (std::is_same<typename Self::CoeffReturnType, float>::value ||
801 std::is_same<typename Self::CoeffReturnType, double>::value ||
802 (std::is_same<typename Self::CoeffReturnType, Eigen::half>::value &&
803 reducer_traits<Op, GpuDevice>::PacketAccess));
805 template <
typename OutputType>
806 static bool run(
const Self& self, Op& reducer,
const GpuDevice& device, OutputType* output,
807 typename Self::Index num_coeffs_to_reduce,
typename Self::Index num_preserved_vals) {
808 gpu_assert(HasOptimizedImplementation &&
"Should only be called on doubles, floats or half floats");
809 const Index num_coeffs = array_prod(self.m_impl.dimensions());
811 if (num_coeffs == 0) {
815 if (num_coeffs_to_reduce <= 128) {
819 return InnerReductionLauncher<Self, Op, OutputType, reducer_traits<Op, GpuDevice>::PacketAccess>::run(
820 self, reducer, device, output, num_coeffs_to_reduce, num_preserved_vals);
824template <
int NumPerThread,
typename Self,
typename Reducer,
typename Index>
825__global__ EIGEN_HIP_LAUNCH_BOUNDS_1024
void OuterReductionKernel(Reducer reducer,
const Self input,
826 Index num_coeffs_to_reduce,
827 Index num_preserved_coeffs,
828 typename Self::CoeffReturnType* output) {
829 const Index num_threads = blockDim.x * gridDim.x;
830 const Index thread_id = blockIdx.x * blockDim.x + threadIdx.x;
832 if (gridDim.x == 1) {
833 for (Index i = thread_id; i < num_preserved_coeffs; i += num_threads) {
834 output[i] = reducer.initialize();
840 const Index max_iter = num_preserved_coeffs * numext::div_ceil<Index>(num_coeffs_to_reduce, NumPerThread);
841 for (Index i = thread_id; i < max_iter; i += num_threads) {
842 const Index input_col = i % num_preserved_coeffs;
843 const Index input_row = (i / num_preserved_coeffs) * NumPerThread;
844 typename Self::CoeffReturnType reduced_val = reducer.initialize();
845 const Index max_row = numext::mini(input_row + NumPerThread, num_coeffs_to_reduce);
846 for (Index j = input_row; j < max_row; j++) {
847 typename Self::CoeffReturnType val = input.m_impl.coeff(j * num_preserved_coeffs + input_col);
848 reducer.reduce(val, &reduced_val);
850 atomicReduce(&(output[input_col]), reduced_val, reducer);
854template <
typename Self,
typename Op>
855struct OuterReducer<Self, Op, GpuDevice> {
859 template <
typename T>
860 using IsFloatOrDouble = bool_constant<std::is_same<T, float>::value || std::is_same<T, double>::value>;
862 static constexpr bool HasOptimizedImplementation =
863 !Self::ReducerTraits::IsStateful && IsFloatOrDouble<typename Self::CoeffReturnType>::value;
864 template <
typename Device,
typename OutputType>
866#if !defined(EIGEN_HIPCC)
879 run(
const Self&, Op&,
const Device&, OutputType*,
typename Self::Index,
typename Self::Index) {
880 gpu_assert(
false &&
"Should only be called to reduce doubles or floats on a gpu device");
887 template <
typename OutputType, EIGEN_SFINAE_ENABLE_IF(IsFloatOrDouble<OutputType>::value)>
888 static bool run(
const Self& self, Op& reducer,
const GpuDevice& device, OutputType* output,
889 typename Self::Index num_coeffs_to_reduce,
typename Self::Index num_preserved_vals) {
890 typedef typename Self::Index Index;
893 if (num_coeffs_to_reduce <= 32) {
899#if defined(EIGEN_CUDACC)
900 constexpr bool has_native_double_sum = std::is_same<Op, SumReducer<double>>::value;
902 constexpr bool has_native_double_sum =
false;
904 if (std::is_same<OutputType, double>::value && !has_native_double_sum) {
905 const Index multi_processors = device.getNumGpuMultiProcessors();
906 if (num_coeffs_to_reduce < 64 || num_preserved_vals < 8 * multi_processors ||
907 num_preserved_vals > 64 * multi_processors) {
912 const Index num_coeffs = num_coeffs_to_reduce * num_preserved_vals;
913 const int block_size = 256;
914 const int num_per_thread = 16;
915 const int dyn_blocks = numext::div_ceil<int>(num_coeffs, block_size * num_per_thread);
916 const int max_blocks = device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor() / block_size;
917 const int num_blocks = numext::mini<int>(max_blocks, dyn_blocks);
919 if (num_blocks > 1) {
922 const int dyn_blocks2 = numext::div_ceil<int>(num_preserved_vals, 1024);
923 const int max_blocks2 = device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor() / 1024;
924 const int num_blocks2 = numext::mini<int>(max_blocks2, dyn_blocks2);
925 LAUNCH_GPU_KERNEL((ReductionInitKernel<OutputType, Index>), num_blocks2, 1024, 0, device, reducer.initialize(),
926 num_preserved_vals, output);
929 LAUNCH_GPU_KERNEL((OuterReductionKernel<num_per_thread, Self, Op, Index>), num_blocks, block_size, 0, device,
930 reducer, self, num_coeffs_to_reduce, num_preserved_vals, output);
Namespace containing all symbols from the Eigen library.