Cuda讀書筆記之shared memory

轉自 http://blog.csdn.net/abcjennifer/article/details/42528569


下面通過一個經典例子來看shared memory作用:矩陣乘法

目的:實現C=A*B,方法:c[i,j] = A[i,:] * B[:,j], 

其中矩陣用row-major表示,即c[i,j] = *(c.elements + i*c.width + j)



1. 不用shared memory優化版:

設A爲m*t的矩陣;B爲t*n的矩陣;

每個線程讀取A的一行,B的一列,計算C的對應值;

所以這樣需要從global memory中讀n次A,m次B。

[cpp] view plain copy
  1. // Matrices are stored in row-major order:  
  2. // M(row, col) = *(M.elements + row * M.width + col)  
  3. typedef struct {  
  4.     int width;  
  5.     int height;  
  6.     float* elements;  
  7. } Matrix;  
  8.   
  9. // Thread block size  
  10. #define BLOCK_SIZE 16  
  11.   
  12. // Forward declaration of the matrix multiplication kernel  
  13. __global__ void MatMulKernel(const Matrix, const Matrix, Matrix);  
  14.   
  15. // Matrix multiplication - Host code  
  16. // Matrix dimensions are assumed to be multiples of BLOCK_SIZE  
  17. void MatMul(const Matrix A, const Matrix B, Matrix C)  
  18. {  
  19.     // Load A and B to device memory  
  20.     Matrix d_A;  
  21.     d_A.width = A.width; d_A.height = A.height;  
  22.     size_t size = A.width * A.height * sizeof(float);  
  23.     cudaMalloc(&d_A.elements, size);  
  24.     cudaMemcpy(d_A.elements, A.elements, size,  
  25.     cudaMemcpyHostToDevice);  
  26.     Matrix d_B;  
  27.     d_B.width = B.width; d_B.height = B.height;  
  28.     size = B.width * B.height * sizeof(float);  
  29.     cudaMalloc(&d_B.elements, size);  
  30.     cudaMemcpy(d_B.elements, B.elements, size,  
  31.     cudaMemcpyHostToDevice);  
  32.   
  33.     // Allocate C in device memory  
  34.     Matrix d_C;  
  35.     d_C.width = C.width; d_C.height = C.height;  
  36.     size = C.width * C.height * sizeof(float);  
  37.     cudaMalloc(&d_C.elements, size);  
  38.   
  39.     // Invoke kernel  
  40.     dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);  
  41.     dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);  
  42.     MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);  
  43.   
  44.     // Read C from device memory  
  45.     cudaMemcpy(C.elements, Cd.elements, size,  
  46.     cudaMemcpyDeviceToHost);  
  47.     }  
  48.   
  49.     // Free device memory  
  50.     cudaFree(d_A.elements);  
  51.     cudaFree(d_B.elements);  
  52.     cudaFree(d_C.elements);  
  53. }  
  54.   
  55. // Matrix multiplication kernel called by MatMul()  
  56. __global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)  
  57. {  
  58.     // Each thread computes one element of C  
  59.     // by accumulating results into Cvalue  
  60.     float Cvalue = 0;  
  61.     int row = blockIdx.y * blockDim.y + threadIdx.y;  
  62.     int col = blockIdx.x * blockDim.x + threadIdx.x;  
  63.     for (int e = 0; e < A.width; ++e)  
  64.     Cvalue += A.elements[row * A.width + e]* B.elements[e * B.width + col];  
  65.     C.elements[row * C.width + col] = Cvalue;  
  66. }  





2. 利用shared memory

每個thread block負責計算一個子矩陣Csub, 其中每個thread負責計算Csub中的一個元素。如下圖所示。爲了將fit設備資源,A,B都分割成很多block_size維的方形matrix,Csub將這些方形matrix的乘積求和而得。每次計算一個乘積時,先將兩個對應方形矩陣從global memory 載入 shared memory(一個thread負責載入A, B兩個sub matrix的元素),然後每個thread計算乘積的一個元素,再由每個thread將這些product加和,存入一個register,最後一次性寫入global memory。計算時注意同步,詳見代碼。


設A爲m*t的矩陣;B爲t*n的矩陣;

這樣呢,A只從global memory讀了n/block_size次,B只讀了m/block_size次;





Kernel Code:

[cpp] view plain copy
  1. __global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)  
  2. {  
  3.     // Block row and column  
  4.     int blockRow = blockIdx.y;  
  5.     int blockCol = blockIdx.x;  
  6.   
  7.     // Each thread block computes one sub-matrix Csub of C  
  8.     Matrix Csub = GetSubMatrix(C, blockRow, blockCol);  
  9.   
  10.     // Each thread computes one element of Csub by accumulating results into Cvalue  
  11.   
  12.     float Cvalue = 0;  
  13.   
  14.     // Thread row and column within Csub  
  15.     int row = threadIdx.y;  
  16.     int col = threadIdx.x;  
  17.   
  18.     // Loop over all the sub-matrices of A and B that are  
  19.     // required to compute Csub  
  20.     // Multiply each pair of sub-matrices together  
  21.     // and accumulate the results  
  22.   
  23.     for (int m = 0; m < (A.width / BLOCK_SIZE); ++m) {  
  24.   
  25.         // Get sub-matrix Asub of A  
  26.         Matrix Asub = GetSubMatrix(A, blockRow, m);  
  27.         // Get sub-matrix Bsub of B  
  28.         Matrix Bsub = GetSubMatrix(B, m, blockCol);  
  29.   
  30.         // Shared memory used to store Asub and Bsub respectively  
  31.         __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];  
  32.         __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];  
  33.   
  34.         // Load Asub and Bsub from device memory to shared memory  
  35.         // Each thread loads one element of each sub-matrix  
  36.         As[row][col] = GetElement(Asub, row, col);  
  37.         Bs[row][col] = GetElement(Bsub, row, col);  
  38.   
  39.         // Synchronize to make sure the sub-matrices are loaded  
  40.         // before starting the computation  
  41.         __syncthreads();  
  42.   
  43.         // Multiply Asub and Bsub together  
  44.         for (int e = 0; e < BLOCK_SIZE; ++e)  
  45.             Cvalue += As[row][e] * Bs[e][col];  
  46.   
  47.         // Synchronize to make sure that the preceding  
  48.         // computation is done before loading two new  
  49.         // sub-matrices of A and B in the next iteration  
  50.         __syncthreads();  
  51.     }  
  52.   
  53.     // Write Csub to device memory  
  54.     // Each thread writes one element  
  55.     SetElement(Csub, row, col, Cvalue);  
  56. }  




Host Code:

[cpp] view plain copy
  1. // Invoke kernel  
  2. dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);  
  3. dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);  
  4. MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章