我想優化我在CUDA中的直方圖計算。它使我在相應的OpenMP CPU計算上有了很好的加速。但是,我懷疑(按照直覺)大多數像素都屬於幾個桶。出於論點的緣故,假設我們有256個像素落入讓我們說,兩個桶。加快CUDA原子計算的許多箱/幾個箱
做到這一點最簡單的方法是它似乎是
- 負載變量到共享內存中
- 如果需要做量化的負載爲無符號字符等做。
- 做一個原子加入共享內存
- 做全球聚結寫。
像這樣:
__global__ void shmem_atomics_reducer(int *data, int *count){
uint tid = blockIdx.x*blockDim.x + threadIdx.x;
__shared__ int block_reduced[NUM_THREADS_PER_BLOCK];
block_reduced[threadIdx.x] = 0;
__syncthreads();
atomicAdd(&block_reduced[data[tid]],1);
__syncthreads();
for(int i=threadIdx.x; i<NUM_BINS; i+=NUM_BINS)
atomicAdd(&count[i],block_reduced[i]);
}
這個內核的性能下降(自然),當我們在32個箱減少箱櫃的數目,從大約45 GB/s到大約10 GB/s的1 bin。爭用和共享內存銀行衝突是理由。我不知道是否有任何方法可以以任何重要的方式去除這些計算中的任何一個。
我也一直在使用__ballot獲取warp結果,然後使用__popc()來執行warp級別降低,從而使用_parallelforall博客中的另一個(美麗)想法來實現warp級別降低。
__global__ void ballot_popc_reducer(int *data, int *count){
uint tid = blockIdx.x*blockDim.x + threadIdx.x;
uint warp_id = threadIdx.x >> 5;
//need lane_ids since we are going warp level
uint lane_id = threadIdx.x%32;
//for ballot
uint warp_set_bits=0;
//to store warp level sum
__shared__ uint warp_reduced_count[NUM_WARPS_PER_BLOCK];
//shared data
__shared__ uint s_data[NUM_THREADS_PER_BLOCK];
//load shared data - could store to registers
s_data[threadIdx.x] = data[tid];
__syncthreads();
//suspicious loop - I think we need more parallelism
for(int i=0; i<NUM_BINS; i++){
warp_set_bits = __ballot(s_data[threadIdx.x]==i);
if(lane_id==0){
warp_reduced_count[warp_id] = __popc(warp_set_bits);
}
__syncthreads();
//do warp level reduce
//could use shfl, but it does not change the overall picture
if(warp_id==0){
int t = threadIdx.x;
for(int j = NUM_WARPS_PER_BLOCK/2; j>0; j>>=1){
if(t<j) warp_reduced_count[t] += warp_reduced_count[t+j];
__syncthreads();
}
}
__syncthreads();
if(threadIdx.x==0){
atomicAdd(&count[i],warp_reduced_count[0]);
}
}
}
這給了體面的數量(當然,這是沒有實際意義 - 峯值裝置MEM體重是133 GB /秒,事情似乎取決於啓動配置)爲單箱情況下(35-40 GB/s的1 bin,而使用原子的則爲10-15 GB/s),但是當我們增加bin的數量時,性能會急劇下降。當我們運行32個bin時,性能下降到大約5 GB/s。原因可能是因爲單線程遍歷所有的bin,要求並行化NUM_BINS循環。
我已經嘗試了幾種並行化NUM_BINS循環的方法,其中沒有一個似乎正常工作。例如,人們可以(非常不恰當地)操作內核爲每個bin創建一些塊。這似乎表現了相同的方式,可能是因爲我們將再次遭受試圖從全局內存中讀取的多個塊的爭用。此外,編程是笨重的。同樣,在y方向上並行化分箱也給出了類似的不令人鼓舞的結果。
我嘗試過的另一個想法是動態並行,爲每個bin啓動一個內核。這個速度很慢,可能是由於沒有真正的子核心計算工作和發射開銷。
最有前途的方法似乎是 - 從尼古拉斯·張伯倫article
使用含箱在共享內存每個線程,這表面上是對SHMEM使用非常沉重的這些所謂的私有化直方圖(只有我們Maxwell上每SM有48 kB)。
也許有人可以對此問題有所瞭解?我覺得應該改變算法,不要使用直方圖,要使用頻率較低的東西。否則,我想我們只是使用原子版本。
編輯:我的問題的上下文是計算概率密度函數用於模式分類。我們可以通過使用非參數方法(如Parzen Windows或Kernel Density Estimation)來計算近似直方圖(更準確地說是pdf)。然而,這並沒有克服維數問題,因爲我們需要對每個箱的所有數據點進行求和,當箱數變大時這變得很昂貴。看到這裏:Parzen
至於你的問題表示,它可能應該關閉,由於「不清楚你的問題「 - 你在某些地方有點模糊,特別是關於你的問題的確切限制,你希望在」讓我們說「的例子中發生的事情等等。此外,你基本上要求一個意見,而不是一個問題的具體答案,這是另一個關閉的理由。但是,我現在正在親自處理幾乎相同的事情,所以我很偏頗。無論如何,我真的想提供我的意見 - 場外。 – einpoklum
如果您想進一步討論,我已經創建了一個[room](http://chat.stackoverflow.com/rooms/125842)。 – einpoklum
當輸入中有很多簡併時,我實際上正在尋找關於直方圖和原子計算的樣式指南的內容。開心討論。 – kakrafoon