CUDA之atomic原子操作詳解
CUDA的原子操作可以理解為對一個變數進行“讀取-修改-寫入”這三個操作的一個最小單位的執行過程,這個執行過程不能夠再分解為更小的部分,在它執行過程中,不允許其他並行執行緒對該變數進行讀取和寫入的操作。基於這個機制,原子操作實現了對在多個執行緒間共享的變數的互斥保護,確保任何一次對變數的操作的結果的正確性。
原子操作確保了在多個並行執行緒間共享的記憶體的讀防寫,每次只能有一個執行緒對該變數進行讀寫操作,一個執行緒對該變數操作的時候,其他執行緒如果也要操作該變數,只能等待前一執行緒執行完成。原子操作確保了安全,代價是犧牲了效能。
CUDA支援多種原子操作,常用的如下:
1、 atomicAdd()
int atomicAdd(int* address, int val);
unsigned int atomicAdd(unsigned int* address,unsigned int val);
unsigned long long int atomicAdd(unsigned long long int* address,unsigned long long int val);
讀取位於全域性或共享儲存器中地址address 處的32 位或64 位字old,計算(old + val),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。只有全域性儲存器支援64 位字。
2、 atomicSub()
int atomicSub(int* address, int val);
unsigned int atomicSub(unsigned int* address, unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算(old - val),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
3、 atomicExch()
int atomicExch(int* address, int val);
unsigned int atomicExch(unsigned int* address,unsigned int val);
unsigned long long int atomicExch(unsigned long long int* address,unsigned long long int val);
float atomicExch(float* address, float val);
讀取位於全域性或共享儲存器中地址address 處的32 位或64 位字old,並將val 儲存在儲存器的同一地址中。這兩項操作在一次原子事務中執行。該函式將返回old。只有全域性儲存器支援64 位字。
4、 atomicMin()
int atomicMin(int* address, int val);
unsigned int atomicMin(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算old 和val 的最小值,並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
5、 atomicMax()
int atomicMax(int* address, int val);
unsigned int atomicMax(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算old 和val 的最大值,並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
6、 atomicInc()
unsigned int atomicInc(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算 ((old >= val) ? 0 : (old+1)),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
7、 atomicDec()
unsigned int atomicDec(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算 (((old == 0) | (old > val)) ? val : (old-1)),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
8、 atomicCAS()
int atomicCAS(int* address, int compare, int val);
unsigned int atomicCAS(unsigned int* address,unsigned int compare,unsigned int val);
unsigned long long int atomicCAS(unsigned long long int* address,unsigned long long int compare,unsigned long long int val);
讀取位於全域性或共享儲存器中地址address 處的32 位或64 位字old,計算 (old == compare ? val : old),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old(比較並交換)。只有全域性儲存器支援64 位字。
9、 atomicAnd()
int atomicAnd(int* address, int val);
unsigned int atomicAnd(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算 (old & val),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
10、 atomicOr()
int atomicOr(int* address, int val);
unsigned int atomicOr(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算 (old | val),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
11、 atomicXor()
int atomicXor(int* address, int val);
unsigned int atomicXor(unsigned int* address,unsigned int val);
讀取位於全域性或共享儲存器中地址address 處的32 位字old,計算 (old ^ val),並將結果儲存在儲存器的同一地址中。這三項操作在一次原子事務中執行。該函式將返回old。
舉個例子,定義1024個執行緒,求這1024個執行緒的ID之和,每個執行緒都會訪問總和變數sum,如果不加原子操作,執行結果是錯誤並且是不確定的。
[cpp] view plain copy print?- #include <stdio.h>
- #include <stdlib.h>
- #include <cuda_runtime.h>
- #define SIZE 1024
- __global__ void histo_kernel(int size, unsigned int *histo)
- {
- int i = threadIdx.x + blockIdx.x * blockDim.x;
- if (i < size)
- {
- //*histo+=i;
- atomicAdd(histo, i);
- }
- }
- int main(void)
- {
- int threadSum = 0;
- //分配記憶體並拷貝初始資料
- unsigned int *dev_histo;
- cudaMalloc((void**)&dev_histo, sizeof(int));
- cudaMemcpy(dev_histo, &threadSum, sizeof(int), cudaMemcpyHostToDevice);
- // kernel launch - 2x the number of mps gave best timing
- cudaDeviceProp prop;
- cudaGetDeviceProperties(&prop, 0);
- int blocks = prop.multiProcessorCount;
- //確保執行緒數足夠
- histo_kernel << <blocks * 2, (SIZE + 2 * blocks - 1) / blocks / 2 >> > (SIZE, dev_histo);
- //資料拷貝回CPU記憶體
- cudaMemcpy(&threadSum, dev_histo, sizeof(int), cudaMemcpyDeviceToHost);
- printf("Threads SUM:%d\n", threadSum);
- getchar();
- cudaFree(dev_histo);
- return 0;
- }