对于上述矩阵乘法来说,每个C[i][j]需要K次乘法+(K- 1)次加法,约等于2K FLOPs,总的FLOPs=M x N x 2K = 2MNK;对于主存访问量,读取A,需要M x K x 4字节;读取B,需要K x N x 4字节;写入C,需要M x N x 4字节,总字节数=4(MK + KN + MN)
voidmulti_cpu(float *C, float *A, float *B, int M, int K, int N) { for (int i = 0; i < M; ++i) { for (int j = 0; j < N; ++j) { float sum = 0.0f; for (int k = 0; k < K; ++k) { sum += A[k + i * K] * B[j + k * N]; } C[j + i * N] = sum; } } }
// 每个线程处理一个输出元素 __global__ voidsgemm_kernel1(float *C, constfloat *A, constfloat *B, int M, int N, int K) { int col = threadIdx.x + blockDim.x * blockIdx.x; int row = threadIdx.y + blockDim.y * blockIdx.y; if (row >= M || col >= N) return;
float val = 0.0f; for (int k = 0; k < K; ++k) { // C[row][col] += A[row][k] * B[k][col] val += A[k + row * K] * B[col + k * N]; } C[col + row * N] = val; }
intmain(int argc, char **argv) { int M = 2048, K = 4096, N = 3072;
size_t Abytes = (size_t)M * K * sizeof(float); size_t Bbytes = (size_t)K * N * sizeof(float); size_t Cbytes = (size_t)M * N * sizeof(float);
// warmup sgemm_kernel1<<<gridDim, blockDim>>>(d_C, d_A, d_B, M, N, K); CHECK_CUDA(cudaGetLastError()); CHECK_CUDA(cudaMemcpy(h_C, d_C, Cbytes, cudaMemcpyDeviceToHost)); check_results(h_C, h_ref, M * N, 5e-4f);
int repeat = argc > 1 ? atoi(argv[1]) : 20; CudaTimer timer; cuda_timer_start(&timer); for (int r = 0; r < repeat; r++) { sgemm_kernel1<<<gridDim, blockDim>>>(d_C, d_A, d_B, M, N, K); CHECK_CUDA(cudaGetLastError()); } float ms = cuda_timer_stop(&timer); float avg_ms = ms / repeat; double gflops = 2.0 * M * N * K / (avg_ms * 1e6); printf("avg %.3f ms | %.1f GFLOPS | ", avg_ms, gflops);
__global__ voidsgemm_kernel2(float *C, constfloat *A, constfloat *B, int M, int N, int K) { int tx = threadIdx.x; int ty = threadIdx.y; int col = tx + blockDim.x * blockIdx.x; int row = ty + blockDim.y * blockIdx.y;
// warmup sgemm_kernel2<<<gridDim, blockDim>>>(d_C, d_A, d_B, M, N, K); CHECK_CUDA(cudaGetLastError()); CHECK_CUDA(cudaMemcpy(h_C, d_C, Cbytes, cudaMemcpyDeviceToHost)); check_results(h_C, h_ref, M * N, 5e-4f);
int repeat = argc > 1 ? atoi(argv[1]) : 20; CudaTimer timer; cuda_timer_start(&timer); for (int r = 0; r < repeat; r++) { sgemm_kernel2<<<gridDim, blockDim>>>(d_C, d_A, d_B, M, N, K); CHECK_CUDA(cudaGetLastError()); } float ms = cuda_timer_stop(&timer); float avg_ms = ms / repeat; double gflops = 2.0 * M * N * K / (avg_ms * 1e6); printf("avg %.3f ms | %.1f GFLOPS | ", avg_ms, gflops);