在OpenCL程式設計中,特别是基于GPU的opencl的程式設計,提高程式性能最主要的方法就是想法提高memory的使用率,它主要包括兩方面的優化:一方面是提高global memory的合并讀寫效率,另一方面就是減少local memory的bank conflict。下面我們分析一下教程7中的代碼,看看它的memory使用率如何?
首先我們用amd的opencl profiler分析一下程式性能[最新的app sdk改用CodeXL進行性能分析]。
下面我們來分析我們的kernel代碼中memory操作:
首先是shared memory的的初始化,我們知道shared memory是local memory,被一個workgroup中的所有thread,或者說work item共享。在amd硬體系統中,local memory是LDS,它通常是為32k或更高,分為32個bank,dword位元組位址,每個bank 512個item,我們可以通過函數得到自己系統中的local memory數量:
cl_ulong DeviceLocalMemSize;
clGetDeviceInfo(device,
CL_DEVICE_LOCAL_MEM_SIZE,
sizeof(cl_ulong),
&DeviceLocalMemSize,
NULL);
LDS的示意圖如下,對于每個bank,同時隻能有一個讀寫請求,如果兩個thread都讀寫bank1,那個必須串行通路,這就稱作bank conflict。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuEWMhZTZ0QzN2gDNiFmYmNzMyYjNyU2MyIGO2cjMxYmMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
kernel初始化local memory的代碼如下:
//初始化共享記憶體
for(int i = 0; i < BIN_SIZE; ++i)
sharedArray[localId * BIN_SIZE + i] = 0;
在同一時間,thread0通路位址0(bank1),thread1,通路位址256,也在bank1,…,這樣就有很多bank conflit,降低程式的性能。從profiler裡面可以看到,lds bank conflit為13.98,很高的比例,是以此時同時運作的thread就比較少,隻有總wave(每個wave 64個thread)的12%(我曾經預設lds記憶體配置設定是0,這樣我們就省去了這些代碼,但是實際上配置設定記憶體是一些随機的值…)。
第二段memory操作的代碼為:
//計算thread直方圖
for(int i = 0; i < BIN_SIZE; ++i)
{
uint value = (uint)data[groupId * groupSize * BIN_SIZE + i * groupSize + localId];
sharedArray[localId * BIN_SIZE + value]++;
}
其中有lds的操作,也有global memory的操作,對于全局memory的通路,在同一時刻,thread0通路i=0的memory
,thread1通路相鄰的memory單元…,這是對于global memory的通路會采用合并讀寫的方式(coalencing),就是一個memory請求傳回16個dword,也就是一個請求滿足16個thread,提高memory使用率。此時對lds的寫是随機的,根據value的值決定,不能控制…
最後一段memory讀寫的代碼:
//合并workgroup中所有線程的直方圖,産生workgroup直方圖
for(int i = 0; i < BIN_SIZE / groupSize; ++i)
{
uint binCount = 0;
for(int j = 0; j < groupSize; ++j)
binCount += sharedArray[j * BIN_SIZE + i * groupSize + localId];
binResult[groupId * BIN_SIZE + i * groupSize + localId] = binCount;
}
其中lds的讀寫如下圖,此時每個線程通路不同的bank,因為amd lds通路就是以32為機關,是以實際上,這段代碼不會有bank conflit。