Skip to content

Commit b526a7f

Browse files
committed
GPU: make the Metal atomics operate on plain counters
GPUAtomic() expanded to metal::atomic, but the counters it guards are plain integers in structures that the host allocates and transfers, and the code reads them directly outside the atomic operations. That is exactly the CUDA and HIP situation, where GPUAtomic() is a no-op and atomicAdd() takes a plain pointer, so Metal now does the same. MSL's atomic_*_explicit do want metal::atomic, which has the same size and alignment, so the operand is cast at the one point where the operation is issued. That cast has to be made in the address space the pointer is really in, since casting a threadgroup pointer to a device atomic is a hard error, hence the two overloads: threadgroup stays threadgroup for the *Shared entry points, anything else resolves to device.
1 parent 6a6ccfe commit b526a7f

2 files changed

Lines changed: 25 additions & 6 deletions

File tree

‎GPU/Common/GPUCommonDefAPI.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@
173173
#define GPUdouble() float
174174
#define GPUbarrier() threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup)
175175
#define GPUbarrierWarp() simdgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup)
176-
#define GPUAtomic(type) atomic<type> // atomic variable type
176+
#define GPUAtomic(type) type // atomic variable type
177177
#elif defined(__HIPCC__) //Defines for HIP
178178
#define GPUd() __device__
179179
#define GPUdDefault() __device__

‎GPU/Common/GPUCommonMath.h‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,25 @@ GPUhdi() constexpr int32_t GPUCommonMath::Abs<int32_t>(int32_t x)
489489
return GPUCA_CHOICE(abs(x), abs(x), abs(x));
490490
}
491491

492+
#ifdef __METAL__
493+
// The counters these operate on are plain integers in the transferred structures,
494+
// as they are for CUDA and HIP. MSL's atomic operations want metal::atomic, which
495+
// has the same size and alignment. The overload is picked by address space, and a
496+
// threadgroup pointer has to keep its own: casting one to a device atomic is an
497+
// error while the qualifier is visible, but a generic pointer resolves here to
498+
// device and the cast then goes through silently.
499+
template <class T>
500+
GPUdi() threadgroup metal::atomic<T>* GPUCommonMathMetalAtomic(threadgroup T* p)
501+
{
502+
return reinterpret_cast<threadgroup metal::atomic<T>*>(p);
503+
}
504+
template <class T>
505+
GPUdi() device metal::atomic<T>* GPUCommonMathMetalAtomic(T* p)
506+
{
507+
return (device metal::atomic<T>*)p;
508+
}
509+
#endif
510+
492511
template <class S, class T>
493512
GPUdi() uint32_t GPUCommonMath::AtomicExchInternal(S* addr, T val)
494513
{
@@ -499,7 +518,7 @@ GPUdi() uint32_t GPUCommonMath::AtomicExchInternal(S* addr, T val)
499518
#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
500519
return ::atomicExch(addr, val);
501520
#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
502-
return atomic_exchange_explicit(addr, val, memory_order_relaxed);
521+
return atomic_exchange_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
503522
#elif defined(WITH_OPENMP)
504523
uint32_t old;
505524
__atomic_exchange(addr, &val, &old, __ATOMIC_SEQ_CST);
@@ -519,7 +538,7 @@ GPUdi() bool GPUCommonMath::AtomicCASInternal(S* addr, T cmp, T val)
519538
#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
520539
return ::atomicCAS(addr, cmp, val) == cmp;
521540
#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
522-
return atomic_compare_exchange_weak_explicit(addr, &cmp, val, memory_order_relaxed, memory_order_relaxed);
541+
return atomic_compare_exchange_weak_explicit(GPUCommonMathMetalAtomic(addr), &cmp, val, memory_order_relaxed, memory_order_relaxed);
523542
#elif defined(WITH_OPENMP)
524543
return __atomic_compare_exchange(addr, &cmp, &val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
525544
#else
@@ -537,7 +556,7 @@ GPUdi() uint32_t GPUCommonMath::AtomicAddInternal(S* addr, T val)
537556
#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
538557
return ::atomicAdd(addr, val);
539558
#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
540-
return atomic_fetch_add_explicit(addr, val, memory_order_relaxed);
559+
return atomic_fetch_add_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
541560
#elif defined(WITH_OPENMP)
542561
return __atomic_add_fetch(addr, val, __ATOMIC_SEQ_CST) - val;
543562
#else
@@ -555,7 +574,7 @@ GPUdi() void GPUCommonMath::AtomicMaxInternal(S* addr, T val)
555574
#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
556575
::atomicMax(addr, val);
557576
#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
558-
atomic_fetch_max_explicit(addr, val, memory_order_relaxed);
577+
atomic_fetch_max_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
559578
#else
560579
S current;
561580
while ((current = *(volatile S*)addr) < val && !AtomicCASInternal(addr, current, val)) {
@@ -573,7 +592,7 @@ GPUdi() void GPUCommonMath::AtomicMinInternal(S* addr, T val)
573592
#elif defined(GPUCA_GPUCODE) && (defined(__CUDACC__) || defined(__HIPCC__))
574593
::atomicMin(addr, val);
575594
#elif defined(GPUCA_GPUCODE) && defined(__METAL__)
576-
atomic_fetch_min_explicit(addr, val, memory_order_relaxed);
595+
atomic_fetch_min_explicit(GPUCommonMathMetalAtomic(addr), val, memory_order_relaxed);
577596
#else
578597
S current;
579598
while ((current = *(volatile S*)addr) > val && !AtomicCASInternal(addr, current, val)) {

0 commit comments

Comments
 (0)