CUDA syncthreads() 阻塞其他线程

时间:2021-06-14 04:18:43

标签: cuda

#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() 导致了问题,但我不知道如何解决。我的代码与其他站点的其他矩阵乘法代码几乎相同。

你能给我一些提示如何解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

每当您在处理 CUDA 代码时遇到问题,我建议您使用 proper CUDA error checking 并使用 compute-sanitizercuda-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 没有报告错误。