我们将按维度的降序 (z, y, x) 表示多维数据。这种顺序与 gridDim 和 blockDim 维度中数据维度的顺序相反!!!
实际上,由于现代计算机中使用二维存储空间,C 语言中的所有多维数组都是线性化的。虽然可以使用如 Pin_d[j][i] 这样的多维数组语法访问多维数组的元素,但编译器将这些访问转换为指向数组开始元素的基指针,以及从这些多维索引计算出的一维偏移量。
至少有两种方法可以对二维数组进行线性化。将同一行/列的所有元素放置到连续的位置。然后将行/列一个接一个地放入内存空间中。这种排列称为行/列主序布局 (row/column-major layout). CUDA C 使用行主序布局。
// we have 3 channels corresponding to RGB
// The input image is encoded as unsigned characters [0, 255]
__global__voidcolorToGreyscaleConversion(unsignedchar*Pout,unsignedchar*Pin,intwidth,intheight){intCol=threadIdx.x+blockIdx.x*blockDim.x;intRow=threadIdx.y+blockIdx.y*blockDim.y;if(Col<width&&Row<height){// get 1D coordinate for the grayscale image
intgreyOffset=Row*width+Col;// one can think of the RGB image having
// CHANNEL times columns than the grayscale image
intrgbOffset=greyOffset*CHANNELS;unsignedcharr=Pin[rgbOffset+0];// red value for pixel
unsignedcharg=Pin[rgbOffset+1];// green value for pixel
unsignedcharb=Pin[rgbOffset+2];// blue value for pixel
// perform the rescaling and store it
// We multiply by floating point constants
Pout[grayOffset]=0.21f*r+0.71f*g+0.07f*b;}}
__global__voidblurKernel(unsignedchar*in,unsignedchar*out,intwidth,intheight){intCol=threadIdx.x+blockIdx.x*blockDim.x;intRow=threadIdx.y+blockIdx.y*blockDim.y;if(Col<width&&Row<height){intpixVal=0;intpixels=0;// Get the average of the surrounding BLUR_SIZE x BLUR_SIZE box
for(intblurRow=-BLUR_SIZE;blurRow<BLUR_SIZE+1;blurRow++){for(intblurCol=-BLUR_SIZE;blurCol<BLUR_SIZE+1;blurCol++){intcurRow=Row+blurRow;intcurCol=Col+blurCol;// If the pixel is within the image, add its value to the sum
if(curRow>-1&&curRow<height&&curCol>-1&&curCol<width){pixVal+=in[curRow*width+curCol];pixels++;// Keep track of the number of pixels in the avg
}}}// Write our new pixel value out
out[Row*width+Col]=(unsignedchar)(pixVal/pixels);}}
矩阵乘法是 Basic Linear Algebra Subprograms (BLAS) 的重要组成部分。
Level 1 形如 $y = \alpha x + y$ 的向量运算。
Level 2 形如 $y = \alpha Ax + \beta y$ 的矩阵-向量运算。
Level 3 形如 $y = \alpha AB + \beta C$ 的矩阵-矩阵运算。
为了用 CUDA 实现矩阵乘法,我们可以采取与 colorToGrayscaleConversion 相同的方法将网格中的线程映射到输出矩阵 P 的元素,即每个线程负责计算 P 中的一个元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Assuming square matrices of size Width x Width
__global__voidMatrixMulKernel(float*M,float*N,float*P,intWidth){// Calculate the row index of the P element and M
intRow=blockIdx.y*blockDim.y+threadIdx.y;// Calculate the column index of P and N
intCol=blockIdx.x*blockDim.x+threadIdx.x;if((Row>=Width)||(Col>=Width))return;floatPvalue=0;// each thread computes one element of the block sub-matrix
for(intk=0;k<Width;++k)Pvalue+=M[Row*Width+k]*N[k*Width+Col];P[Row*Width+Col]=Pvalue;}