最近進行gpu編程,受困於雜事,對threadIdx blockIdx blockDim理解存在偏頗導致浪費不少時日。遂整理資料加深認識。
以下是最常見的gpu圖像遍歷方式:
dim3 dimBlock( 線程數1, 線程數2,1) ;//最開始,望文生義而把後面數據理解爲 threadIdx 的值導致大量時間浪費在這上面。這邊就是線程數,不是塊數。不要被網上代碼迷惑
dim3 dimGrid( ceil( ( 圖像寬 + 線程數1 - 1)/線程數1 ) ,ceil( ( 圖像高 + 線程數 2-1) /線程數2) , 1 ) ;
Cal<<<dimGrid , dimBlock>>>(圖像數據首地址 …… );
__global__ void Cal(unsigned char * srcData ……)
{
const int idx = threadIdx.x + blockIdx.x * blockDim.x;
const int idy = threadIdx.y + blockIdx.y * blockDim.y;
…………
if(idx < 圖像寬 && idy < 圖像高) //圖像寬 ,圖像高通過參數傳入
{
<span style="white-space: pre;"> </span>……//對圖像進行相應單像素操作
}
}
按照上面訪問方式 就可以進行圖像逐像素遍歷。 各個元素解釋如下:
dim3 : 定義如下
struct __device_builtin__ dim3
{
unsigned int x, y, z;
#if defined(__cplusplus)
__host__ __device__ dim3(unsigned int vx = 1, unsigned int vy = 1, unsigned int vz = 1) : x(vx), y(vy), z(vz) {}
__host__ __device__ dim3(uint3 v) : x(v.x), y(v.y), z(v.z) {}
__host__ __device__ operator uint3(void) { uint3 t; t.x = x; t.y = y; t.z = z; return t; }
#endif /* __cplusplus */
};
blockDim.x : 塊操作x方向維數 , 對應 ceil( ( 圖像寬 + 線程數1 - 1)/線程數1 ) 所在位置指定值。
blockDim.y : 快操作y方向維數 , 對應 ceil( ( 圖像高 + 線程數 2-1) /線程數2) 所在位置指定值。
threadIdx.x : 當前線程 x 方向值 , 範圍 0~ 線程數1 -1 。
threadIdx.y : 當前線程y方向值 , 範圍 0~ 線程數2 - 1 。
blockIdx.x : 當前塊 x方向值 , 範圍 0~ceil( ( 圖像寬 + 線程數1 - 1)/線程數1 ) - 1
blockIdx.y : 當前塊 y方向值 , 範圍 0~ceil( ( 圖像高 + 線程數 2-1) /線程數2) - 1
通過以上 知識,可以計算得到 idx 的最小值= 0 , idx最大值<= 圖像寬 + 線程數1 - 1 ,並且遍歷 最大最小值之間所有值,因此可以把寬度方向遍歷完畢。同理,y方向也剛好每行遍歷一次,不存在重複遍歷元素情況。
在之後,因爲有共有參數,嫌這樣方式慢,我更改爲二維方式訪問。頻繁出錯,出錯原因就在於 忽略 “遍歷 最大最小值之間所有值” 這個條件導致出錯。
最後遍歷方式:
//塊,線程設置
dim3 dimBlock(線程數1, 線程數2,1) ;
dim3 dimGrid(1, 塊數2, 1 ) ;
//調用
Cal<<<dimGrid , dimBlock>>>(圖像數據首地址 …… );
__global__ void Cal(unsigned char * srcData ……)
{
const int idx = threadIdx.x ;
const int idy = threadIdx.y + blockIdx.y * blockDim.y ;
//載入共享數據
……
for(int dy = idy + offsetLoc;dy < 圖像高; dy += 線程數2*blockDim.y)
{
for(int dx = idx ; dx < 圖像寬; dx += 線程數1)
{
//逐像素操作
}
}
}
之所以按照上面訪問方式,是因爲有使用共享內存的需要。並不是最優方案,按照自己需求更改訪問方式。