cuda 原子鎖 多執行緒操作 通用原子操作

2021-10-12 03:23:21 字數 4083 閱讀 4240

在專案中,空間中有200w+的點,需要對映到乙個grid_map的600*600的網格中,落入到同乙個格仔的點需要進行一些計算獲得乙個值。對於格仔與格仔之間是並行的,但格仔之中的點需要設計為序列。所以在計算某個格仔中的點時,需要將格仔的值保護起來,只允許乙個執行緒(點)計算並改變。

這裡就用到了cuda的通用原子操作。也許有人會問,cuda提供了一些原子操作函式,能不能直接用呢?cuda提供的原子函式適用於簡單的單一變數判斷加減,而對於需要複雜的計算操作是力不從心的。但其實,我們要實現的通用原子操作也是基於cuda的原子函式,我們進行一些設計就可以得到想要的通用原子操作,比如鎖。

在《gpu高效能程式設計cuda實戰》一書中,提到了通用原子操作的鎖的設計,貼上原始碼:

struct lock 

~lock

(void

) __device__ void

lock

(void

) __device__ void

unlock

(void)}

;...

....

__global__ void

thekernel

(lock mylock)

這裡通過atomiccasatomicexch兩個函式進行設計,但乙個執行緒lock之後,將mutex置為1,其他執行緒將在while處迴圈等待,直到該執行緒unlock,將mutex重新置於0,剩下的執行緒中再次爭奪鎖。

但是這個結構是存在問題的,我在測試時候發現呼叫thekernel<<<128, 1>>>(lock)可以正常執行,而thekernel<<<1, 128>>>(lock)出現了死鎖,也就是在block中線程數大於1情況中,出現死鎖。百思不得其解…後來查到了出現這種情況的原因:

cuda執行是以wrap為單位進行的,也就是說乙個wrap中32個執行緒中的乙個獲得了鎖,執行完了lock,按理說該執行緒要繼續執行do_your_job()unlock,而現實是執行緒都卡在了lock處。這就是因為wrap的同步執行規則(locked-step execution),換句話說,乙個wrap的執行緒是同步執行乙個函式,並同步退出乙個函式。獲得鎖的執行緒在lock函式結束處苦苦等待其他31個執行緒兄弟一起進入do_your_job(),而剩下的31個執行緒卻等著它unlock釋放鎖,所以出現了死鎖。而每個block中只有乙個執行緒則不會出現死鎖,是因為此時wrap中僅有乙個執行緒。

顯然,這個設計方法並不滿足我的需求。

考慮到同乙個wrap的執行緒都是『同進退共生死』,那麼我們只能在那個獲得鎖的執行緒退出函式前,就釋放了鎖。看**:

__global__ void

kernel1()

}}intmain()

在程式中,獲得鎖的執行緒進入到if中,並在執行完if之前就釋放了鎖,這樣就解決了同乙個wrap出現死鎖的情況。當然,這樣的寫法不怎麼優美且不魯棒…(但是能用)。另外,這個函式換成這樣寫法就不行了:

__global__ void

kernel2()

}}intmain()

這是因為break在不同的機器和編譯器中,不能都保證是先釋放了鎖再break出來,可能被編譯器優化成其他形式。可以看出這種cuda通用原子操作確實比較蛋疼。

不過我在專案中採取了這種方法,將mlock由int變為int陣列,就可以實現多把鎖並行,提高效率,貼上我執行ok的**:

__device__ void

docriticjob

(int thread_index,

float

* mprocess)

__global__ void

kernel2

(int

* mflag,

float

* mprocess)}}

intmain()

;int h_flag[4]

=;float

*dev_process;

int*dev_flag;

cudastatus =

cudamalloc((

void**

)&dev_process,4*

sizeof

(float))

;if(cudastatus != cudasuccess)

cudastatus =

cudamalloc((

void**

)&dev_flag,4*

sizeof

(int))

;if(cudastatus != cudasuccess)

cudastatus =

cudamemcpy

(dev_process, h_process,4*

sizeof

(float

), cudamemcpyhosttodevice);if

(cudastatus != cudasuccess)

cudastatus =

cudamemcpy

(dev_flag, h_flag,4*

sizeof

(int

), cudamemcpyhosttodevice);if

(cudastatus != cudasuccess)

kernel2<<

<2,

3>>

>

(dev_flag,dev_process)

; cudastatus =

cudadevicesynchronize()

;if(cudastatus != cudasuccess)

float outprocess[4]

; cudastatus =

cudamemcpy

(outprocess, dev_process,4*

sizeof

(float

), cudamemcpydevicetohost);if

(cudastatus != cudasuccess)

for(

float mproces : outprocess)

}

可以實現對多個鎖的控制,鎖之間是並行的。

這個方法的名字是我瞎取的。直接上**:

__device__ volatile

int sem =0;

__device__ void

acquire_semaphore

(volatile

int*lock)

__device__ void

release_semaphore

(volatile

int*lock)..

....

....

__global__ void

inkernel()

為什麼叫仲裁中介呢?因為這裡採用了每個block的乙個執行緒作為中介進行仲裁,在acquire_semaphore中爭奪鎖,一旦某個block的第乙個執行緒獲得了鎖,那麼剩下的block第乙個執行緒將陷入while迴圈中,同時因為__syncthreads(),導致整個block停下。這就實現了以block為單位的序列。但是這其實也不完美,若要再進一步在block的執行緒中實現序列,則要繼續加入條件判斷。可以看stackoverflow:鏈結

cuda 中不可避免的遇到需要序列計算的情況,可以每個方案都不是完美的,需要根據情況進行取捨,我也繼續學習,應該是存在更好的方案,日後遇到了再新增進來。

多執行緒 原子操作

include include includeint sum 0 多個執行緒同時訪問 讀 寫 乙個變數,就會發生衝突。乙個變數的執行緒安全 多個執行緒同時讀寫沒有誤差。解決方法 臨界區 事件機制 互斥量 原子操作 原子操作的 速度快於 臨界區 事件機制 互斥量 原子操作函式,解決多執行緒安全 dwo...

Linux使用者層多執行緒無鎖化原子操作

最近由於專案需要,需要將原先使用的icu4c由4.x版本公升級到5.x,但是利用編譯好的5.x版本的icu4c鏈結庫編譯原先的程式時,報出了類似undefined sync sub and fetch的錯誤,沒辦法,最後通過檢視icu4c的原始碼,加入了一些編譯選項,將相關的 sync x函式預設不...

java 多執行緒 原子操作 一

對於乙個集合 需要在加入之前 執行 先檢查後執行 的步驟以達到 若沒有則新增,若有則不新增 的目的,實行多執行緒以提高效率。notthreadsafe public class listhelper 可惜的是這段 並不能保證能夠正確的完成工作,這是為什麼呢?集合和動作都已經被synchronized...