: 입력 행렬의 데이터 크기가 공유 메모리보다 큰 경우에 어떤 식으로 최적화를 진행할 지를 학습한다.
L1 캐시로 공유 메모리를 사용
: 하드웨어에게 공유 메모리 활용을 맡긴다.
: 이 경우 사용자 관리 캐시로 사용하는 방법만큼 성능 향상 기대하기 어렵다.
전체 데이터가 아닌 일부 데이터만 올려가며 사용
: 어떤 데이터를 어떤 시점에 공유 메모리에 올리고 내릴지를 사용자가 결정.
#define BLOCK_SIZE 16 // 스레드 블록의 한 축 사이즈
...
__global__ void matMul_SharedMem (int* A, int* B, int* C, int m, int n, int k)
{
__shared__ int subA[BLOCK_SIZE][BLOCK_SIZE];
__shared__ int subB[BLOCK_SIZE][BLOCK_SIZE];
...
}
threadIdx.x
, threadIdx.y
) 로 정의, 이를 각각 localRow와 localCol로 정의 int localRow = threadIdx.x;
int localCol = threadIdx.y;
: 실제 읽어와야 하는 전역 메모리상의 위치를 계산 -> 현재 처리중인 데이터 블록의 번호를 bID로 정의 (0,1, ...)
: bID * BLOCK_SIZE = stride 로 정의
SubA[localRow][localCol] = A[row*k + stride + localCol]
SubB[localRow][localCol] = B[(stride + localRow)*n + col]
__global__ void MatMul_SharedMem(DATA_TYPE* matA, DATA_TYPE* matB, int* matC, int m, int n, int k)
{
int row = blockDim.x * blockIdx.x + threadIdx.x;
int col = blockDim.y * blockIdx.y + threadIdx.y;
int val = 0;
__shared__ int subA[BLOCK_SIZE][BLOCK_SIZE];
__shared__ int subB[BLOCK_SIZE][BLOCK_SIZE];
int localRow = threadIdx.x;
int localCol = threadIdx.y;
for (int bID = 0; bID < ceil((float)k / BLOCK_SIZE); bID++) {
int offset = bID * BLOCK_SIZE;
// load A and B
if (row >= m || offset + localCol >= k)
subA[localRow][localCol] = 0;
else
subA[localRow][localCol] = matA[row * k + (offset + localCol)];
if (col >= n || offset + localRow >= k)
subB[localRow][localCol] = 0;
else
subB[localRow][localCol] = matB[(offset + localRow) * n + col];
__syncthreads();
// compute
for (int i = 0; i < BLOCK_SIZE; i++) {
val += subA[localRow][i] * subB[i][localCol];
}
__syncthreads();
}
if (row >= m || col >= n)
return;
matC[row * n + col] = val;
}