__global__ voidmatrix_multiplication_kernel(constfloat *A, constfloat *B, float *C, int M, int N, int K){ int col = blockDim.x * blockIdx.x + threadIdx.x; int row = blockDim.y * blockIdx.y + threadIdx.y; if (col >= K || row >= M) { return; }
// A(N, 1), B(K, 1), C(K, 1) float sum = 0.0f; for (int n = 0; n < N; ++n) { sum += A[row * N + n] * B[n * K + col]; } C[row * K + col] = sum; }
// A, B, C are device pointers (i.e. pointers to memory on the GPU) extern"C"voidsolve(constfloat *A, constfloat *B, float *C, int M, int N, int K){ dim3 threadsPerBlock(16, 16); dim3 blocksPerGrid((K + threadsPerBlock.x - 1) / threadsPerBlock.x, (M + threadsPerBlock.y - 1) / threadsPerBlock.y);
matrix_multiplication_kernel<<<blocksPerGrid, threadsPerBlock>>>(A, B, C, M, N, K); cudaDeviceSynchronize(); }
循环的每次迭代执行一次乘法和一次加法,忽略循环控制开销时,每个输出元素约需要
FLOPs。若从单线程视角估算,并假设每次访问都需要从 Global Memory
读取、不考虑缓存和线程之间的复用,则需要读取 、
各 个 float,共
Bytes,对应的算术强度为
这一数值是朴素访存模型下的上层估算,并不表示硬件一定产生
次彼此独立的内存事务。在同一次循环迭代中,具有相同 row
的相邻线程读取同一个
A[row * N + n],可由缓存或广播机制复用;相邻
col 的线程读取连续的
B[n * K + col],能够形成合并访问,最终写入 C
时地址也连续。不过这些数据没有被程序显式保存在 Block
内供后续线程复用,矩阵较大、缓存不能容纳工作集时,同一元素仍可能被不同线程或
Block 反复从更低层存储读取,因此性能通常受内存流量限制。
__global__ voidmatrix_multiplication_kernel(constfloat *A, constfloat *B, float *C, int M, int N, int K){ int col = blockDim.x * blockIdx.x + threadIdx.x; int row = blockDim.y * blockIdx.y + threadIdx.y;
__shared__ float As[TILE_SIZE][TILE_SIZE]; __shared__ float Bs[TILE_SIZE][TILE_SIZE]; float sum = 0.0f;
// A(N, 1), B(K, 1), C(K, 1) for (int tile = 0; tile < (N + TILE_SIZE - 1) / TILE_SIZE; ++tile) { // A tile int Acol = threadIdx.x + tile * TILE_SIZE; if (row < M && Acol < N) { As[threadIdx.y][threadIdx.x] = A[row * N + Acol]; } else { As[threadIdx.y][threadIdx.x] = 0.0f; } // B tile int Brow = threadIdx.y + tile * TILE_SIZE; if (Brow < N && col < K) { Bs[threadIdx.y][threadIdx.x] = B[Brow * K + col]; } else { Bs[threadIdx.y][threadIdx.x] = 0.0f; } __syncthreads();
for (int n = 0; n < TILE_SIZE; ++n) { sum += As[threadIdx.y][n] * Bs[n][threadIdx.x]; } __syncthreads(); }
if (row < M && col < K) { C[row * K + col] = sum; } }
// A, B, C are device pointers (i.e. pointers to memory on the GPU) extern"C"voidsolve(constfloat *A, constfloat *B, float *C, int M, int N, int K){ dim3 threadsPerBlock(TILE_SIZE, TILE_SIZE); dim3 blocksPerGrid((K + threadsPerBlock.x - 1) / threadsPerBlock.x, (M + threadsPerBlock.y - 1) / threadsPerBlock.y);
matrix_multiplication_kernel<<<blocksPerGrid, threadsPerBlock>>>(A, B, C, M, N, K); cudaDeviceSynchronize(); }
讨论
评论