#define TS 32
int num_devices = 0;
__global__ void shared_kernel(float* A, float* B, float* C, int M, int N, int K) {
int global_col = blockDim.x * blockIdx.x + threadIdx.x;
int global_row = blockDim.y * blockIdx.y + threadIdx.y;
int local_col = threadIdx.x;
int local_row = threadIdx.y;
if (global_row >= M || global_col >= N) return;
__shared__ float Asub[TS][TS];
__shared__ float Bsub[TS][TS];
const int num_tiles = K / TS;
float acc = 0;
for(int t = 0; t < num_tiles; t++){
const int t_row = TS * t + local_row;
const int t_col = TS * t + local_col;
Asub[local_row][local_col] = A[global_row * K + t_col];
Bsub[local_row][local_col] = B[t_row * N + global_col];
__syncthreads();
printf("[DEBUG] first sync threads, global_row: %d, global_col: %d\n", global_row, global_col);
for (int k = 0; k < K; ++k) {
acc += Asub[local_row][k] * Bsub[k][local_col];
}
__syncthreads();
printf("[DEBUG] second sync threads, global_row: %d, global_col: %d\n", global_row, global_col);
}
C[global_row * N + global_col] = acc;
}
static float *a_d, *b_d, *c_d;
void mat_mul(float *A, float *B, float *C, int M, int N, int K) {
cudaMemcpy(a_d, A, M * K * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(b_d, B, K * N * sizeof(float), cudaMemcpyHostToDevice);
dim3 blockDim(TS, TS);
dim3 gridDim(M/TS, N/TS);
shared_kernel<<<gridDim, blockDim>>>(a_d, b_d, c_d, M, N, K);
cudaMemcpy(C, c_d, M * N * sizeof(float), cudaMemcpyDeviceToHost);
cudaDeviceSynchronize();
}
void mat_mul_init(float *A, float *B, float *C, int M, int N, int K) {
cudaGetDeviceCount(&num_devices);
cudaSetDevice(0);
cudaMalloc(&a_d, M * K * sizeof(float));
cudaMalloc(&b_d, K * N * sizeof(float));
cudaMalloc(&c_d, M * N * sizeof(float));
}
上面的例子是一个共享内存的矩阵乘法。
我用 dim3 blockDim(TS, TS)
和 dim3 gridDim(M/TS, N/TS)
以及 M、N、K = 128 在内核上运行。
我在启动内核后检查 float * C
的值为零。另外,我发现在第一个 __syncthreads()
之后只打印了很少的 global_row(从 37 到 81),并且在第二个 printf
之后没有 __syncthreads()
DEBUG 消息。
我怀疑是 __syncthreads()
导致了问题,但我不知道如何解决。我的代码与其他站点的其他矩阵乘法代码几乎相同。
你能给我一些提示如何解决这个问题吗?
答案 0 :(得分:2)
每当您在处理 CUDA 代码时遇到问题,我建议您使用 proper CUDA error checking 并使用 compute-sanitizer
或 cuda-memcheck
运行您的代码。对于这种类型的分析,如果您不使用内核中的 printf
会更容易。
如果你这样做了,你会看到这样的输出:
========= Invalid __shared__ read of size 4
========= at 0x000002f0 in shared_kernel(float*, float*, float*, int, int, int)
========= by thread (0,2,0) in block (0,1,0)
========= Address 0x00002000 is out of bounds
========= Saved host backtrace up to driver entry point at kernel launch time
... (and more output)
因此,我们可以看到您的内核正在执行无效的 __shared__
读取操作。这在您的内核中发生在哪里?您可以使用方法 here 来识别特定的代码行。然而,这是一个相当简单的内核,只有一行是从共享内存中读取的,它在这里:
for (int k = 0; k < K; ++k) {
acc += Asub[local_row][k] * Bsub[k][local_col]; // shared reads here
快速检查将显示,如果您让此循环在 K=128
的范围内迭代,那么您将在这里索引越界:
for (int k = 0; k < K; ++k) {
acc += Asub[local_row][k] * Bsub[k][local_col];
^ ^
当 k
大于 31 时,因为这会超出您共享的数组维度:
#define TS 32
__shared__ float Asub[TS][TS];
__shared__ float Bsub[TS][TS];
我不打算为您编写固定的内核/代码,因为正如您已经指出的那样,该主题在许多其他地方都有涉及,并且在 the programming guide 中已经提供了一个规范示例.
FWIW,如果我将您的 for 循环更改为:
for (int k = 0; k < TS; ++k) {
然后运行时错误就消失了。 cuda-memcheck
没有报告错误。