Skip to content

Commit 9ac4611

Browse files
committed
GPU: implement the work-group and warp collectives for Metal
Metal fell through to the CPU branch of GPUCommonAlgorithm.h, where a scan, a reduction or a broadcast returns the thread's own value: right for one thread per block, wrong for the 32- to 1024-thread groups Metal runs. The TPC ZS decoding and the stream compaction of the cluster finder got wrong offsets and counts, and found no clusters at all. Implement them with MSL's SIMD-group functions, and the work-group ones by scanning within each SIMD group and then across the group totals, which go through smem as with cub. MSL has the thread index only as a kernel parameter, so like smem it is taken from the caller's scope, and the CfUtils block helpers get it through GPUCA_THREAD_INFO_DECL, which is empty for the other backends. blockPredicateScan loses the default of its last parameter for that. Sums are returned promoted, since the callers pass !!pred and a bool would truncate them. The CUDA, HIP, OpenCL and CPU code is unchanged.
1 parent 4c92364 commit 9ac4611

6 files changed

Lines changed: 120 additions & 9 deletions

File tree

‎GPU/Common/GPUCommonAlgorithm.h‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,111 @@ GPUdi() T warp_reduce_max(T v)
500500
return __syncthreads_count(pred);
501501
}
502502

503+
#elif defined(__METAL__)
504+
// MSL has SIMD-group collectives only, so the work-group ones scan within each
505+
// SIMD group and then across the group totals, which go through smem as with
506+
// cub. The thread index is a kernel parameter in MSL, so like smem it is taken
507+
// from the caller's scope. A 1D threadgroup is split into SIMD groups of
508+
// consecutive threads, of GPUCA_WARP_SIZE each. The SIMD scans take 32-bit
509+
// lanes; the narrower types are counts and are widened. Sums are returned
510+
// promoted, as v + 0 would be, since the callers pass !!pred: a bool would
511+
// truncate them to 0 / 1.
512+
513+
template <class T>
514+
GPUdi() auto warp_scan_inclusive_add(T v) -> decltype(+v)
515+
{
516+
return simd_prefix_inclusive_sum((int32_t)v);
517+
}
518+
519+
template <class T>
520+
GPUdi() T warp_broadcast(T v, int32_t i)
521+
{
522+
return (T)simd_shuffle((int32_t)v, (ushort)i);
523+
}
524+
525+
template <class T>
526+
GPUdi() T warp_shuffle_xor(T v, int32_t laneMask)
527+
{
528+
return simd_shuffle_xor(v, (ushort)laneMask);
529+
}
530+
531+
template <int32_t Width, class T>
532+
GPUdi() T warp_reduce_add(T v)
533+
{
534+
for (int32_t m = Width / 2; m > 0; m >>= 1) {
535+
v += warp_shuffle_xor(v, m);
536+
}
537+
return v;
538+
}
539+
540+
template <int32_t Width, class T>
541+
GPUdi() T warp_reduce_min(T v)
542+
{
543+
for (int32_t m = Width / 2; m > 0; m >>= 1) {
544+
const T o = warp_shuffle_xor(v, m);
545+
v = o < v ? o : v;
546+
}
547+
return v;
548+
}
549+
550+
template <int32_t Width, class T>
551+
GPUdi() T warp_reduce_max(T v)
552+
{
553+
for (int32_t m = Width / 2; m > 0; m >>= 1) {
554+
const T o = warp_shuffle_xor(v, m);
555+
v = o > v ? o : v;
556+
}
557+
return v;
558+
}
559+
560+
#define work_group_scan_inclusive_add(v) work_group_scan_inclusive_add_FUNC(v, smem, iThread, nThreads)
561+
template <class T, class S>
562+
GPUdi() auto work_group_scan_inclusive_add_FUNC(T v, S& smem, int32_t iThread, int32_t nThreads) -> decltype(+v)
563+
{
564+
const int32_t iWarp = iThread / GPUCA_WARP_SIZE;
565+
const int32_t nWarps = (nThreads + GPUCA_WARP_SIZE - 1) / GPUCA_WARP_SIZE;
566+
int32_t x = simd_prefix_inclusive_sum((int32_t)v);
567+
if (iThread % GPUCA_WARP_SIZE == GPUCA_WARP_SIZE - 1 || iThread == nThreads - 1) {
568+
smem.warpPredicateSum[iWarp] = x;
569+
}
570+
GPUbarrier();
571+
if (iWarp == 0) {
572+
const int32_t s = simd_prefix_inclusive_sum(iThread < nWarps ? smem.warpPredicateSum[iThread] : 0);
573+
if (iThread < nWarps) {
574+
smem.warpPredicateSum[iThread] = s;
575+
}
576+
}
577+
GPUbarrier();
578+
if (iWarp > 0) {
579+
x += smem.warpPredicateSum[iWarp - 1];
580+
}
581+
GPUbarrier();
582+
return x;
583+
}
584+
585+
#define work_group_broadcast(v, i) work_group_broadcast_FUNC(v, i, smem, iThread)
586+
template <class T, class S>
587+
GPUdi() T work_group_broadcast_FUNC(T v, int32_t i, S& smem, int32_t iThread)
588+
{
589+
if (iThread == i) {
590+
smem.tmpBroadcast = v;
591+
}
592+
GPUbarrier();
593+
T retVal = smem.tmpBroadcast;
594+
GPUbarrier();
595+
return retVal;
596+
}
597+
598+
#define work_group_reduce_add(v) work_group_reduce_add_FUNC(v, smem, iThread, nThreads)
599+
template <class T, class S>
600+
GPUdi() auto work_group_reduce_add_FUNC(T v, S& smem, int32_t iThread, int32_t nThreads) -> decltype(+v)
601+
{
602+
return work_group_broadcast_FUNC(work_group_scan_inclusive_add_FUNC(v, smem, iThread, nThreads), nThreads - 1, smem, iThread);
603+
}
604+
605+
#define work_group_count(pred) ((uint32_t)work_group_reduce_add_FUNC((int32_t)(pred), smem, iThread, nThreads))
606+
#define work_group_any(pred) (work_group_count(pred) != 0)
607+
503608
#else
504609
// Trivial implementation for the CPU
505610

‎GPU/GPUTracking/Base/GPUGeneralKernels.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ class GPUKernelTemplate
7676
int32_t tmpBroadcast;
7777
int32_t warpPredicateSum[I / GPUCA_WARP_SIZE];
7878
};
79+
#elif defined(__METAL__) && defined(GPUCA_GPUCODE)
80+
// What the Metal work-group collectives in GPUCommonAlgorithm.h go through
81+
union {
82+
int32_t tmpBroadcast;
83+
int32_t warpPredicateSum[I / GPUCA_WARP_SIZE];
84+
};
7985
#endif
8086
};
8187

‎GPU/GPUTracking/TPCClusterFinder/CfUtils.h‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class CfUtils
6565
}
6666

6767
template <size_t BlockSize, typename SharedMemory>
68-
static GPUdi() int32_t blockPredicateScan(SharedMemory& smem, int32_t pred, int32_t* sum = nullptr)
68+
static GPUdi() int32_t blockPredicateScan(SharedMemory& smem, int32_t pred, int32_t* sum GPUCA_THREAD_INFO_DECL)
6969
{
7070
#if defined(__HIPCC__) || defined(__CUDACC__)
7171
int32_t iThread =
@@ -115,7 +115,7 @@ class CfUtils
115115
}
116116

117117
template <size_t BlockSize, typename SharedMemory>
118-
static GPUdi() int32_t blockPredicateSum(SharedMemory& smem, int32_t pred)
118+
static GPUdi() int32_t blockPredicateSum(SharedMemory& smem, int32_t pred GPUCA_THREAD_INFO_DECL)
119119
{
120120
#if defined(__HIPCC__) || defined(__CUDACC__)
121121
int32_t iThread =
@@ -152,12 +152,12 @@ class CfUtils
152152
}
153153

154154
template <size_t SCRATCH_PAD_WORK_GROUP_SIZE, typename SharedMemory>
155-
static GPUdi() uint16_t partition(SharedMemory& smem, uint16_t ll, bool pred, uint16_t partSize, uint16_t* newPartSize)
155+
static GPUdi() uint16_t partition(SharedMemory& smem, uint16_t ll, bool pred, uint16_t partSize, uint16_t* newPartSize GPUCA_THREAD_INFO_DECL)
156156
{
157157
bool participates = ll < partSize;
158158

159159
int32_t part;
160-
int32_t lpos = blockPredicateScan<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, int32_t(!pred && participates), &part);
160+
int32_t lpos = blockPredicateScan<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, int32_t(!pred && participates), &part GPUCA_THREAD_INFO_PROVIDE);
161161

162162
uint16_t pos = (participates && !pred) ? lpos : part;
163163

‎GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDeconvolution.cxx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ GPUdii() void GPUTPCCFDeconvolution::deconvolutionImpl(int32_t nBlocks, int32_t
5252

5353
uint16_t in3x3 = 0;
5454
bool exclude3x3 = iamPeak || !pos.valid();
55-
partId = CfUtils::partition<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, ll, exclude3x3, SCRATCH_PAD_WORK_GROUP_SIZE, &in3x3);
55+
partId = CfUtils::partition<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, ll, exclude3x3, SCRATCH_PAD_WORK_GROUP_SIZE, &in3x3 GPUCA_THREAD_INFO_PROVIDE);
5656

5757
if (partId < in3x3) {
5858
smem.posBcast1[partId] = pos;
@@ -76,7 +76,7 @@ GPUdii() void GPUTPCCFDeconvolution::deconvolutionImpl(int32_t nBlocks, int32_t
7676
}
7777

7878
uint16_t in5x5 = 0;
79-
partId = CfUtils::partition<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, partId, peakCount > 0 && !exclude3x3, in3x3, &in5x5);
79+
partId = CfUtils::partition<SCRATCH_PAD_WORK_GROUP_SIZE>(smem, partId, peakCount > 0 && !exclude3x3, in3x3, &in5x5 GPUCA_THREAD_INFO_PROVIDE);
8080

8181
if (partId < in5x5) {
8282
smem.posBcast1[partId] = pos;

‎GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ GPUdii() bool GPUTPCCFPeakFinder::isPeak(
5050
ll,
5151
belowThreshold,
5252
SCRATCH_PAD_WORK_GROUP_SIZE,
53-
&lookForPeaks);
53+
&lookForPeaks GPUCA_THREAD_INFO_PROVIDE);
5454

5555
if (partId < lookForPeaks) {
5656
posBcast[partId] = pos;

‎GPU/GPUTracking/TPCClusterFinder/GPUTPCCFStreamCompaction.cxx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ GPUdii() void GPUTPCCFStreamCompaction::Thread<GPUTPCCFStreamCompaction::scanSta
3636
pred = predicate[iThreadGlobal];
3737
}
3838

39-
int32_t nElemsInBlock = CfUtils::blockPredicateSum<GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE>(smem, pred);
39+
int32_t nElemsInBlock = CfUtils::blockPredicateSum<GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE>(smem, pred GPUCA_THREAD_INFO_PROVIDE);
4040

4141
int32_t lastThread = nThreads - 1;
4242
if (iThread == lastThread) {
@@ -115,7 +115,7 @@ GPUdii() void GPUTPCCFStreamCompaction::Thread<GPUTPCCFStreamCompaction::compact
115115
bool iAmDummy = (iThreadGlobal >= nElems);
116116

117117
int32_t pred = (iAmDummy) ? 0 : predicate[iThreadGlobal];
118-
int32_t offsetInBlock = CfUtils::blockPredicateScan<GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE>(smem, pred);
118+
int32_t offsetInBlock = CfUtils::blockPredicateScan<GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE>(smem, pred, nullptr GPUCA_THREAD_INFO_PROVIDE);
119119

120120
SizeT globalOffsetOut = offsetInBlock;
121121
if (iBlock > 0) {

0 commit comments

Comments
 (0)