CUDA syncthreads() 阻塞其他线程

CUDA syncthreads() block other threads

#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),并且在第二个 __syncthreads() 之后没有 printf DEBUG 消息。

我怀疑是 __syncthreads() 引起了问题,但我不知道如何解决。我的代码和其他网站的其他矩阵乘法代码几乎一样。

你能给我一些解决方法的提示吗?

任何时候您在使用 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];

我不会费心为你写一个固定的kernel/code,因为正如你已经指出的,这个主题在许多其他地方都有涉及,并且已经在[中提供了一个规范的例子=24=].

FWIW,如果我将您的 for 循环更改为:

   for (int k = 0; k < TS; ++k) {

然后 运行-时间错误对我来说消失了。 cuda-memcheck 报告没有错误。