Atomics#
Single OS thread + cooperative fibers: fibers only ever switch at an explicit __syncthreads()/warp sync, never mid-expression, so a plain read-modify-write IS atomic with respect to every other thread in the block. Each returns the OLD value, like the real CUDA intrinsics. The value argument is a non-deduced context (type_identity_t) so atomicAdd(&ull, 1) etc. convert like CUDA’s per-type overloads instead of failing template deduction.
Defined in code/simulator.h.
This page documents 11 symbols: 11 functions.
Functions#
Signature |
Description |
Availability |
Location |
Example |
|---|---|---|---|---|
|
Single OS thread + cooperative fibers: fibers only ever switch at an explicit __syncthreads()/warp sync, never mid-expression, so a plain read-modify-write IS atomic with respect to every other … |
sim only |
simulator.h:963 |
|
|
sim only |
simulator.h:975 |
||
|
A FAILED atomicCAS is the signature of a spin-wait, so it yields: that is what lets a device spin-lock ( |
sim only |
simulator.h:981 |
|
|
sim only |
simulator.h:996 |
||
|
atomicExch yields when the exchange changed nothing, i.e. |
sim only |
simulator.h:967 |
|
|
atomicInc/Dec are unsigned-only wrap counters, matching CUDA’s semantics. |
sim only |
simulator.h:993 |
|
|
sim only |
simulator.h:974 |
||
|
sim only |
simulator.h:973 |
||
|
sim only |
simulator.h:976 |
||
|
sim only |
simulator.h:964 |
||
|
sim only |
simulator.h:977 |
Examples#
Code from the repository’s example corpus (code\*.cu); the highlighted line is the call site. These blocks are what the View links above open.
atomicAdd (test_atomics.cu:38)
28 } while (0)
29
30constexpr int N = 4096; // number of contributing threads
31constexpr int BLOCK = 128;
32constexpr int BLOCKS = N / BLOCK;
33constexpr int BINS = 16;
34
35// ---- Kernels ---------------------------------------------------------------
36
37__global__ void kCounterInt(int* c) {
38 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicAdd(c, 1);
39}
40__global__ void kCounterUll(unsigned long long* c) {
41 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicAdd(c, 1ULL);
42}
43__global__ void kCounterFloat(float* c) {
44 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicAdd(c, 1.0f);
45}
46__global__ void kCounterDouble(double* c) {
47 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicAdd(c, 1.0);
48}
atomicAnd (test_atomics.cu:78)
68}
69
70// 32 threads each set their own bit via atomicOr -> full mask.
71__global__ void kBitmaskOr(unsigned* mask) {
72 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
73 if (i < 32u) atomicOr(mask, 1u << i);
74}
75// Start all-ones; each of 32 threads clears its bit via atomicAnd -> 0.
76__global__ void kBitmaskAnd(unsigned* mask) {
77 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
78 if (i < 32u) atomicAnd(mask, ~(1u << i));
79}
80// XOR each value in; final = running parity of all values.
81__global__ void kXor(const unsigned* data, unsigned* acc) {
82 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
83 if (i < static_cast<unsigned>(N)) atomicXor(acc, data[i]);
84}
85
86__global__ void kSub(int* c) {
87 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicSub(c, 1);
88}
atomicCAS (test_atomics.cu:67)
57 if (i < static_cast<unsigned>(N)) atomicMax(out, data[i]);
58}
59__global__ void kMin(const int* data, int* out) {
60 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
61 if (i < static_cast<unsigned>(N)) atomicMin(out, data[i]);
62}
63
64// Exactly one thread should win the compare-and-swap for the sentinel.
65__global__ void kClaimOnce(int* owner, int* winners) {
66 const int i = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
67 if (i < N && atomicCAS(owner, -1, i) == -1) atomicAdd(winners, 1);
68}
69
70// 32 threads each set their own bit via atomicOr -> full mask.
71__global__ void kBitmaskOr(unsigned* mask) {
72 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
73 if (i < 32u) atomicOr(mask, 1u << i);
74}
75// Start all-ones; each of 32 threads clears its bit via atomicAnd -> 0.
76__global__ void kBitmaskAnd(unsigned* mask) {
77 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
78 if (i < 32u) atomicAnd(mask, ~(1u << i));
atomicDec (test_web_cs_atomics.cu:44)
34__global__ void testKernel(int *g_odata)
35{
36 const unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;
37
38 atomicAdd(&g_odata[0], 10);
39 atomicSub(&g_odata[1], 10);
40 atomicExch(&g_odata[2], tid);
41 atomicMax(&g_odata[3], tid);
42 atomicMin(&g_odata[4], tid);
43 atomicInc((unsigned int *)&g_odata[5], 17);
44 atomicDec((unsigned int *)&g_odata[6], 137);
45 atomicCAS(&g_odata[7], tid - 1, tid);
46 atomicAnd(&g_odata[8], 2 * tid + 7);
47 atomicOr(&g_odata[9], 1 << tid);
48 atomicXor(&g_odata[10], tid);
49}
50
51// verbatim CPU reference from simpleAtomicIntrinsics_cpu.cpp (computeGold), with
52// its printf diagnostics kept. Returns true iff every atomic matched.
53// [SIM] max/min are provided by simulator.h (exact-type overloads; builtins under nvcc).
54static bool computeGold(int *gpuData, const int len)
55{
atomicExch (test_web_cbe_hashtable.cu:55)
45 Lock() {
46 HANDLE_ERROR(cudaMalloc((void**)&mutex, sizeof(int)));
47 HANDLE_ERROR(cudaMemset(mutex, 0, sizeof(int)));
48 }
49 __device__ void lock() {
50 while (atomicCAS(mutex, 0, 1) != 0);
51 __threadfence();
52 }
53 __device__ void unlock() {
54 __threadfence();
55 atomicExch(mutex, 0);
56 }
57};
58
59#define SIZE (64 * 1024) // [SIM] was 100*1024*1024
60#define ELEMENTS (SIZE / sizeof(unsigned int))
61#define HASH_ENTRIES 1024
62
63struct Entry {
64 unsigned int key;
65 void* value;
66 Entry* next;
atomicInc (test_web_cs_atomics.cu:43)
33// verbatim device kernel from simpleAtomicIntrinsics_kernel.cuh
34__global__ void testKernel(int *g_odata)
35{
36 const unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;
37
38 atomicAdd(&g_odata[0], 10);
39 atomicSub(&g_odata[1], 10);
40 atomicExch(&g_odata[2], tid);
41 atomicMax(&g_odata[3], tid);
42 atomicMin(&g_odata[4], tid);
43 atomicInc((unsigned int *)&g_odata[5], 17);
44 atomicDec((unsigned int *)&g_odata[6], 137);
45 atomicCAS(&g_odata[7], tid - 1, tid);
46 atomicAnd(&g_odata[8], 2 * tid + 7);
47 atomicOr(&g_odata[9], 1 << tid);
48 atomicXor(&g_odata[10], tid);
49}
50
51// verbatim CPU reference from simpleAtomicIntrinsics_cpu.cpp (computeGold), with
52// its printf diagnostics kept. Returns true iff every atomic matched.
53// [SIM] max/min are provided by simulator.h (exact-type overloads; builtins under nvcc).
54static bool computeGold(int *gpuData, const int len)
atomicMax (test_atomics.cu:57)
47 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicAdd(c, 1.0);
48}
49
50__global__ void kHistogram(const int* data, unsigned* hist) {
51 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
52 if (i < static_cast<unsigned>(N)) atomicAdd(&hist[data[i] % BINS], 1u);
53}
54
55__global__ void kMax(const int* data, int* out) {
56 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
57 if (i < static_cast<unsigned>(N)) atomicMax(out, data[i]);
58}
59__global__ void kMin(const int* data, int* out) {
60 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
61 if (i < static_cast<unsigned>(N)) atomicMin(out, data[i]);
62}
63
64// Exactly one thread should win the compare-and-swap for the sentinel.
65__global__ void kClaimOnce(int* owner, int* winners) {
66 const int i = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
67 if (i < N && atomicCAS(owner, -1, i) == -1) atomicAdd(winners, 1);
68}
atomicMin (test_atomics.cu:61)
51 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
52 if (i < static_cast<unsigned>(N)) atomicAdd(&hist[data[i] % BINS], 1u);
53}
54
55__global__ void kMax(const int* data, int* out) {
56 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
57 if (i < static_cast<unsigned>(N)) atomicMax(out, data[i]);
58}
59__global__ void kMin(const int* data, int* out) {
60 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
61 if (i < static_cast<unsigned>(N)) atomicMin(out, data[i]);
62}
63
64// Exactly one thread should win the compare-and-swap for the sentinel.
65__global__ void kClaimOnce(int* owner, int* winners) {
66 const int i = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
67 if (i < N && atomicCAS(owner, -1, i) == -1) atomicAdd(winners, 1);
68}
69
70// 32 threads each set their own bit via atomicOr -> full mask.
71__global__ void kBitmaskOr(unsigned* mask) {
72 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
atomicOr (test_atomics.cu:73)
63// Exactly one thread should win the compare-and-swap for the sentinel.
64__global__ void kClaimOnce(int* owner, int* winners) {
65 const int i = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
66 if (i < N && atomicCAS(owner, -1, i) == -1) atomicAdd(winners, 1);
67}
68
69// 32 threads each set their own bit via atomicOr -> full mask.
70__global__ void kBitmaskOr(unsigned* mask) {
71 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
72 if (i < 32u) atomicOr(mask, 1u << i);
73}
74// Start all-ones; each of 32 threads clears its bit via atomicAnd -> 0.
75__global__ void kBitmaskAnd(unsigned* mask) {
76 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
77 if (i < 32u) atomicAnd(mask, ~(1u << i));
78}
79// XOR each value in; final = running parity of all values.
80__global__ void kXor(const unsigned* data, unsigned* acc) {
81 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
82 if (i < static_cast<unsigned>(N)) atomicXor(acc, data[i]);
83}
atomicSub (test_atomics.cu:87)
77 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
78 if (i < 32u) atomicAnd(mask, ~(1u << i));
79}
80// XOR each value in; final = running parity of all values.
81__global__ void kXor(const unsigned* data, unsigned* acc) {
82 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
83 if (i < static_cast<unsigned>(N)) atomicXor(acc, data[i]);
84}
85
86__global__ void kSub(int* c) {
87 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicSub(c, 1);
88}
89
90// ---- Host driver -----------------------------------------------------------
91
92template <class T>
93static T* devFrom(const T& init) {
94 T* d = nullptr;
95 CUDA_CHECK(cudaMalloc(&d, sizeof(T)));
96 CUDA_CHECK(cudaMemcpy(d, &init, sizeof(T), cudaMemcpyHostToDevice));
97 return d;
98}
atomicXor (test_atomics.cu:83)
73 if (i < 32u) atomicOr(mask, 1u << i);
74}
75// Start all-ones; each of 32 threads clears its bit via atomicAnd -> 0.
76__global__ void kBitmaskAnd(unsigned* mask) {
77 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
78 if (i < 32u) atomicAnd(mask, ~(1u << i));
79}
80// XOR each value in; final = running parity of all values.
81__global__ void kXor(const unsigned* data, unsigned* acc) {
82 const unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
83 if (i < static_cast<unsigned>(N)) atomicXor(acc, data[i]);
84}
85
86__global__ void kSub(int* c) {
87 if (blockIdx.x * blockDim.x + threadIdx.x < N) atomicSub(c, 1);
88}
89
90// ---- Host driver -----------------------------------------------------------
91
92template <class T>
93static T* devFrom(const T& init) {
94 T* d = nullptr;