Project
Loading...
Searching...
No Matches
GPUCommonAlgorithm.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#ifndef GPUCOMMONALGORITHM_H
16#define GPUCOMMONALGORITHM_H
17
18#include "GPUCommonDef.h"
19
20#if !defined(GPUCA_GPUCODE) || defined(__CPUDEVICE__) // Could also enable custom search on the CPU, but it is not always faster, so we stick to std::sort
21#ifndef GPUCA_GPUCODE_DEVICE
22#include <algorithm>
23#endif
24#define GPUCA_ALGORITHM_STD
25#endif
26
27// ----------------------------- SORTING -----------------------------
28
29namespace o2::gpu
30{
32{
33 public:
34 template <class T>
35 GPUd() static void sort(T* begin, T* end);
36 template <class T>
37 GPUd() static void sortInBlock(T* begin, T* end GPUCA_THREAD_INFO_DECL);
38 template <class T>
39 GPUd() static void sortDeviceDynamic(T* begin, T* end);
40 template <class T, class S>
41 GPUd() static void sort(T* begin, T* end, const S& comp);
42 template <class T, class S>
43 GPUd() static void sortInBlock(T* begin, T* end, const S& comp GPUCA_THREAD_INFO_DECL);
44 template <class T, class S>
45 GPUd() static void sortDeviceDynamic(T* begin, T* end, const S& comp);
46#if __cplusplus >= 202002L // FIXME: auto parameter not supported in C++17
47 template <class T, class S>
48 GPUh() static void sortOnDevice(auto* rec, int32_t stream, T* begin, size_t N, const S& comp);
49#endif
50 template <class T>
51 GPUd() static void swap(T& a, T& b);
52 template <class I, class P>
53 GPUhd() static I partitionPoint(I first, I count, const P& pred);
54
55 private:
56 template <typename I>
57 GPUd() static void QuickSort(I f, I l) GPUnoexcept(); // Quicksort implementation
58
59 template <typename I, typename Cmp>
60 GPUd() static void QuickSort(I f, I l, Cmp cmp) GPUnoexcept(); // Quicksort implementation
61
62 template <typename IndexType, typename I, typename Cmp>
63 GPUd() static void QuickSortImpl(I f, I l, Cmp cmp) GPUnoexcept(); // Quicksort implementation with index type IndexType, which must be able to hold the number of elements
64
65 template <typename I, typename Cmp>
66 GPUd() static void InsertionSort(I f, I l, Cmp cmp) GPUnoexcept(); // Insertionsort implementation
67
68 template <typename I, typename Cmp>
69 GPUd() static I MedianOf3Select(I f, I l, Cmp cmp) GPUnoexcept(); // Helper for Quicksort implementation
70
71 template <typename I, typename T, typename Cmp>
72 GPUd() static I UnguardedPartition(I f, I l, T piv, Cmp cmp) GPUnoexcept(); // Helper for Quicksort implementation
73
74 template <typename I>
75 GPUd() static void IterSwap(I a, I b) GPUnoexcept(); // Helper
76};
77
78#ifndef GPUCA_ALGORITHM_STD
79template <typename I>
80GPUdi() void GPUCommonAlgorithm::IterSwap(I a, I b) GPUnoexcept()
81{
82 auto tmp = *a;
83 *a = *b;
84 *b = tmp;
85}
86
87template <typename I, typename Cmp>
88GPUdi() void GPUCommonAlgorithm::InsertionSort(I f, I l, Cmp cmp) GPUnoexcept()
89{
90 auto it0{f};
91 while (it0 != l) {
92 auto tmp{*it0};
93
94 auto it1{it0};
95 while (it1 != f && cmp(tmp, it1[-1])) {
96 it1[0] = it1[-1];
97 --it1;
98 }
99 it1[0] = tmp;
100
101 ++it0;
102 }
103}
104
105template <typename I, typename Cmp>
106GPUdi() I GPUCommonAlgorithm::MedianOf3Select(I f, I l, Cmp cmp) GPUnoexcept()
107{
108 auto m = f + (l - f) / 2;
109
110 --l;
111
112 if (cmp(*f, *m)) {
113 if (cmp(*m, *l)) {
114 return m;
115 } else if (cmp(*f, *l)) {
116 return l;
117 } else {
118 return f;
119 }
120 } else if (cmp(*f, *l)) {
121 return f;
122 } else if (cmp(*m, *l)) {
123 return l;
124 } else {
125 return m;
126 }
127}
128
129template <typename I, typename T, typename Cmp>
130GPUdi() I GPUCommonAlgorithm::UnguardedPartition(I f, I l, T piv, Cmp cmp) GPUnoexcept()
131{
132 do {
133 while (cmp(*f, piv)) {
134 ++f;
135 }
136 --l;
137 while (cmp(piv, *l)) {
138 --l;
139 }
140
141 if (l <= f) {
142 return f;
143 }
144 IterSwap(f, l);
145 ++f;
146 } while (true);
147}
148
149template <typename I, typename Cmp>
150GPUdi() void GPUCommonAlgorithm::QuickSort(I f, I l, Cmp cmp) GPUnoexcept()
151{
152 if (l - f <= 0xFFFF) {
153 QuickSortImpl<uint16_t>(f, l, cmp);
154 } else {
155 QuickSortImpl<uint32_t>(f, l, cmp);
156 }
157}
158
159template <typename IndexType, typename I, typename Cmp>
160GPUdi() void GPUCommonAlgorithm::QuickSortImpl(I f, I l, Cmp cmp) GPUnoexcept()
161{
162 if (f == l) {
163 return;
164 }
165
166 struct pair {
167 IndexType first;
168 IndexType second;
169 };
170
171 struct Stack {
172 pair data[sizeof(IndexType) * 8 - 4]; // The smaller partition is processed first, so the depth is bounded by log2(n / cutoff) + 1, cutoff = 50 > 2^5
173 uint8_t n{0};
174
175 GPUd() void emplace(IndexType x, IndexType y)
176 {
177 data[n++] = {x, y};
178 }
179 GPUd() bool empty() const { return n == 0; }
180 GPUd() pair& top() { return data[n - 1]; }
181 GPUd() void pop() { --n; }
182 };
183
184 Stack s;
185 s.emplace(0, l - f);
186 while (!s.empty()) {
187 const auto it0 = f + s.top().first;
188 const auto it1 = f + s.top().second;
189 s.pop();
190
191 const auto piv = *MedianOf3Select(it0, it1, cmp);
192 const auto pp = UnguardedPartition(it0, it1, piv, cmp);
193
194 constexpr auto cutoff = 50u;
195 const auto lsz = pp - it0;
196 const auto rsz = it1 - pp;
197 if (lsz < rsz) {
198 if (rsz > cutoff) {
199 s.emplace(pp - f, it1 - f);
200 }
201 if (lsz > cutoff) {
202 s.emplace(it0 - f, pp - f);
203 }
204 } else {
205 if (lsz > cutoff) {
206 s.emplace(it0 - f, pp - f);
207 }
208 if (rsz > cutoff) {
209 s.emplace(pp - f, it1 - f);
210 }
211 }
212 }
213 InsertionSort(f, l, cmp);
214}
215
216template <typename I>
217GPUdi() void GPUCommonAlgorithm::QuickSort(I f, I l) GPUnoexcept()
218{
219 QuickSort(f, l, [](auto&& x, auto&& y) { return x < y; });
220}
221#endif
222
224
225} // namespace o2::gpu
226
227#if (((defined(__CUDACC__) && !defined(__clang__)) || defined(__HIPCC__))) && !defined(GPUCA_GPUCODE_HOSTONLY)
228
230
231#else
232
233namespace o2::gpu
234{
235
236template <class T>
237GPUdi() void GPUCommonAlgorithm::sortDeviceDynamic(T* begin, T* end)
238{
239#ifndef GPUCA_GPUCODE
240 GPUCommonAlgorithm::sort(begin, end);
241#else
242 GPUCommonAlgorithm::sortDeviceDynamic(begin, end, [](auto&& x, auto&& y) { return x < y; });
243#endif
244}
245
246template <class T, class S>
247GPUdi() void GPUCommonAlgorithm::sortDeviceDynamic(T* begin, T* end, const S& comp)
248{
249 GPUCommonAlgorithm::sort(begin, end, comp);
250}
251
252} // namespace o2::gpu
253
254#endif // THRUST
255// sort and sortInBlock below are not taken from Thrust, since our implementations are faster
256
257namespace o2::gpu
258{
259
260template <class T>
261GPUdi() void GPUCommonAlgorithm::sort(T* begin, T* end)
262{
263#ifdef GPUCA_ALGORITHM_STD
264 std::sort(begin, end);
265#else
266 QuickSort(begin, end, [](auto&& x, auto&& y) { return x < y; });
267#endif
268}
269
270template <class T, class S>
271GPUdi() void GPUCommonAlgorithm::sort(T* begin, T* end, const S& comp)
272{
273#ifdef GPUCA_ALGORITHM_STD
274 std::sort(begin, end, comp);
275#else
276 QuickSort(begin, end, comp);
277#endif
278}
279
280template <class T>
281GPUdi() void GPUCommonAlgorithm::sortInBlock(T* begin, T* end GPUCA_THREAD_INFO_DECL)
282{
283#ifndef GPUCA_GPUCODE
284 GPUCommonAlgorithm::sort(begin, end);
285#else
286 GPUCommonAlgorithm::sortInBlock(begin, end, [](auto&& x, auto&& y) { return x < y; } GPUCA_THREAD_INFO_PROVIDE);
287#endif
288}
289
290template <class T, class S>
291GPUdi() void GPUCommonAlgorithm::sortInBlock(T* begin, T* end, const S& comp GPUCA_THREAD_INFO_DECL)
292{
293#if !defined(GPUCA_GPUCODE) || defined(GPUCA_DEVICE_SINGLE_THREAD)
294 GPUCommonAlgorithm::sort(begin, end, comp);
295#elif defined(GPUCA_DETERMINISTIC_MODE) // Cannot use GPUCA_DETERMINISTIC_CODE macro here, which is enforced in TPC compression
296 if (get_local_id(0) == 0) {
297 GPUCommonAlgorithm::sort(begin, end, comp);
298 }
299 GPUbarrier();
300#else
301 int32_t n = end - begin;
302 for (int32_t i = 0; i < n; i++) {
303 for (int32_t tIdx = get_local_id(0); tIdx < n; tIdx += get_local_size(0)) {
304 int32_t offset = i % 2;
305 int32_t curPos = 2 * tIdx + offset;
306 int32_t nextPos = curPos + 1;
307
308 if (nextPos < n) {
309 if (!comp(begin[curPos], begin[nextPos])) {
310 IterSwap(&begin[curPos], &begin[nextPos]);
311 }
312 }
313 }
314 GPUbarrier();
315 }
316#endif
317}
318
319#ifdef GPUCA_GPUCODE
320template <class T>
321GPUdi() void GPUCommonAlgorithm::swap(T& a, T& b)
322{
323 auto tmp = a;
324 a = b;
325 b = tmp;
326}
327#else
328template <class T>
329GPUdi() void GPUCommonAlgorithm::swap(T& a, T& b)
330{
331 std::swap(a, b);
332}
333#endif
334
335// std::partition_point over the indices [first, first + count): the first index for which pred is false, pred being true on a prefix.
336// A lower bound of value in a sorted key(i) is pred(i) = key(i) < value, an upper bound pred(i) = !(value < key(i)).
337template <class I, class P>
338GPUhdi() I GPUCommonAlgorithm::partitionPoint(I first, I count, const P& pred)
339{
340 while (count > 0) {
341 const I step = count / 2;
342 const I middle = first + step;
343 if (pred(middle)) {
344 first = middle + 1;
345 count -= step + 1;
346 } else {
347 count = step;
348 }
349 }
350 return first;
351}
352
353} // namespace o2::gpu
354
355// ----------------------------- WORK GROUP FUNCTIONS -----------------------------
356
357#ifdef __OPENCL__
358// Nothing to do, work_group functions available
359#pragma OPENCL EXTENSION cl_khr_subgroups : enable
360
361template <class T>
362GPUdi() T warp_scan_inclusive_add_FUNC(T v)
363{
364 return sub_group_scan_inclusive_add(v);
365}
366template <> // FIXME: It seems OpenCL does not support 8 and 16 bit subgroup operations
367GPUdi() uint8_t warp_scan_inclusive_add_FUNC<uint8_t>(uint8_t v)
368{
369 return sub_group_scan_inclusive_add((uint32_t)v);
370}
371template <class T>
372GPUdi() T warp_broadcast_FUNC(T v, int32_t i)
373{
374 return sub_group_broadcast(v, i);
375}
376template <>
377GPUdi() uint8_t warp_broadcast_FUNC<uint8_t>(uint8_t v, int32_t i)
378{
379 return sub_group_broadcast((uint32_t)v, i);
380}
381
382#define warp_scan_inclusive_add(v) warp_scan_inclusive_add_FUNC(v)
383#define warp_broadcast(v, i) warp_broadcast_FUNC(v, i)
384
385[[nodiscard]] GPUdi() int32_t work_group_count(bool pred)
386{
387 return work_group_reduce_add((int32_t)pred);
388}
389
390#elif (defined(__CUDACC__) || defined(__HIPCC__))
391// CUDA and HIP work the same way using cub, need just different header
392
393#if !defined(GPUCA_GPUCODE_COMPILEKERNELS) && !defined(GPUCA_GPUCODE_HOSTONLY)
394#if defined(__CUDACC__)
395#include <cub/cub.cuh>
396#elif defined(__HIPCC__)
397#include <hipcub/hipcub.hpp>
398#endif
399#endif
400
401#define work_group_scan_inclusive_add(v) work_group_scan_inclusive_add_FUNC(v, smem)
402template <class T, class S>
403GPUdi() T work_group_scan_inclusive_add_FUNC(T v, S& smem)
404{
405 typename S::BlockScan(smem.cubTmpMem).InclusiveSum(v, v);
406 __syncthreads();
407 return v;
408}
409
410#define work_group_broadcast(v, i) work_group_broadcast_FUNC(v, i, smem)
411template <class T, class S>
412GPUdi() T work_group_broadcast_FUNC(T v, int32_t i, S& smem)
413{
414 if ((int32_t)threadIdx.x == i) {
415 smem.tmpBroadcast = v;
416 }
417 __syncthreads();
418 T retVal = smem.tmpBroadcast;
419 __syncthreads();
420 return retVal;
421}
422
423#define work_group_reduce_add(v) work_group_reduce_add_FUNC(v, smem)
424template <class T, class S>
425GPUdi() T work_group_reduce_add_FUNC(T v, S& smem)
426{
427 v = typename S::BlockReduce(smem.cubReduceTmpMem).Sum(v);
428 __syncthreads();
429 v = work_group_broadcast(v, 0);
430 return v;
431}
432
433#define warp_scan_inclusive_add(v) warp_scan_inclusive_add_FUNC(v, smem)
434template <class T, class S>
435GPUdi() T warp_scan_inclusive_add_FUNC(T v, S& smem)
436{
437 typename S::WarpScan(smem.cubWarpTmpMem).InclusiveSum(v, v);
438 return v;
439}
440
441#define warp_broadcast(v, i) warp_broadcast_FUNC(v, i)
442template <class T>
443GPUdi() T warp_broadcast_FUNC(T v, int32_t i)
444{
445#ifdef __CUDACC__
446 return __shfl_sync(0xFFFFFFFF, v, i);
447#else // HIP
448 return __shfl(v, i);
449#endif
450}
451
452template <class T>
453GPUdi() T warp_shuffle_xor(T v, int32_t laneMask)
454{
455#ifdef __CUDACC__
456 return __shfl_xor_sync(0xFFFFFFFF, v, laneMask);
457#else // HIP
458 return __shfl_xor(v, laneMask);
459#endif
460}
461
462// Butterfly all-reduce over groups of Width lanes: every lane gets the result.
463// Width is a power of two and must not exceed the hardware warp size.
464template <int32_t Width, class T>
465GPUdi() T warp_reduce_add(T v)
466{
467 for (int32_t m = Width / 2; m > 0; m >>= 1) {
468 v += warp_shuffle_xor(v, m);
469 }
470 return v;
471}
472
473template <int32_t Width, class T>
474GPUdi() T warp_reduce_min(T v)
475{
476 for (int32_t m = Width / 2; m > 0; m >>= 1) {
477 const T o = warp_shuffle_xor(v, m);
478 v = o < v ? o : v;
479 }
480 return v;
481}
482
483template <int32_t Width, class T>
484GPUdi() T warp_reduce_max(T v)
485{
486 for (int32_t m = Width / 2; m > 0; m >>= 1) {
487 const T o = warp_shuffle_xor(v, m);
488 v = o > v ? o : v;
489 }
490 return v;
491}
492
493[[nodiscard]] GPUdi() bool work_group_any(bool pred)
494{
495 return __syncthreads_or(pred);
496}
497
498[[nodiscard]] GPUdi() uint32_t work_group_count(bool pred)
499{
500 return __syncthreads_count(pred);
501}
502
503#else
504// Trivial implementation for the CPU
505
506template <class T>
507GPUdi() T work_group_scan_inclusive_add(T v)
508{
509 return v;
510}
511
512template <class T>
513GPUdi() T work_group_reduce_add(T v)
514{
515 return v;
516}
517
518template <class T>
519GPUdi() T work_group_broadcast(T v, int32_t i)
520{
521 return v;
522}
523
524template <class T>
525GPUdi() T warp_scan_inclusive_add(T v)
526{
527 return v;
528}
529
530template <class T>
531GPUdi() T warp_broadcast(T v, int32_t i)
532{
533 return v;
534}
535
536template <int32_t Width, class T>
537GPUdi() T warp_reduce_add(T v)
538{
539 return v;
540}
541
542template <int32_t Width, class T>
543GPUdi() T warp_reduce_min(T v)
544{
545 return v;
546}
547
548template <int32_t Width, class T>
549GPUdi() T warp_reduce_max(T v)
550{
551 return v;
552}
553
554[[nodiscard]] GPUdi() bool work_group_any(bool pred)
555{
556 return pred;
557}
558
559[[nodiscard]] GPUdi() uint32_t work_group_count(bool pred)
560{
561 return pred;
562}
563
564#endif
565
566#ifdef GPUCA_ALGORITHM_STD
567#undef GPUCA_ALGORITHM_STD
568#endif
569
570#endif
int32_t i
#define get_local_size(dim)
#define GPUdi()
#define get_local_id(dim)
#define GPUCA_THREAD_INFO_PROVIDE
#define GPUbarrier()
#define GPUhdi()
#define GPUh()
#define GPUCA_THREAD_INFO_DECL
#define GPUd()
int32_t retVal
GPUd() static void sort(T *begin
GPUhd() static I partitionPoint(I first
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
GLint GLsizei count
Definition glcorearb.h:399
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLint first
Definition glcorearb.h:399
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLboolean * data
Definition glcorearb.h:298
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLuint stream
Definition glcorearb.h:1806
uint8_t itsSharedClusterMap uint8_t
GPUdoubleBinary64 b
GPUdi() o2
Definition TrackTRD.h:39
GPUCommonAlgorithm CAAlgo
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:158
value_T step
Definition TrackUtils.h:42
void empty(int)
GPUReconstruction * rec
a move-only header stack with serialized headers This is the flat buffer where all the headers in a m...
Definition Stack.h:33
char const *restrict const cmp
Definition x9.h:96