CUDA合并了二维块的访问

时间:2012-09-09 10:55:09

标签: memory cuda

对于1D案例,我已经非常了解CUDA中全局内存的整个合并访问要求。

然而,我对二维情况有点困惑(即我们有2D网格,由2D块组成)。

假设我有一个向量in_vector,并且在我的内核中我希望以合并的方式访问它。像这样:

__global__ void my_kernel(float* out_matrix, float* in_vector, int size)
{
   int i = blockIdx.x * blockDim.x + threadIdx.x;
   int j = blockIdx.y * blockDim.y + threadIdx.y;
   // ...
   float vx = in_vector[i]; // This is good. Here we have coalesced access
   float vy = in_vector[j]; // Not sure about this. All threads in my warp access the same global address. (See explanation)
   // ...
   // Do some calculations... Obtain result
}

在我对这种2D情况的理解中,块内的线程以列主要方式“排列”。例如:假设(threadIdx.x,threadIdx.y)表示法:

  • 第一个warp将是:(0,0),(1,0),(2,0),...,(31,0),
  • 第二个warp将是:(0,1),(1,1),(2,1),...,(31,1),
  • 等......

在这种情况下,调用in_vector[i]为我们提供了一个合并访问,因为同一warp中的每个连续线程都将访问连续的地址。但是调用in_vector[j]似乎是一个错误的想法,因为每个连续的线程将访问全局内存中的相同地址(例如,warp 0中的所有线程都将访问in_vector [0],这将为我们提供32个不同的全局内存请求)

我是否理解正确?如果是这样,我如何使用in_vector[j]

进行合并访问全局内存

1 个答案:

答案 0 :(得分:7)

您在问题中显示的内容仅适用于某些块大小。您的“合并”访问权限:

int i = blockIdx.x * blockDim.x + threadIdx.x;
float vx = in_vector[i];
仅当in_vector大于或等于32时,

才会导致blockDim.x从全局内存中合并访问。即使在合并的情况下,块中的每个线程共享相同的{{1}值从全局内存中读取相同的单词,这似乎是反直觉和浪费。

确保读取的正确方法是每个线程是唯一的,并且合并的是计算块内的线程数和网格内的偏移量,可能类似于:

threadIdx.x

如果您的意图实际上不是为每个线程读取一个唯一值,那么您可以节省大量内存带宽使用共享内存实现合并,如下所示:

int tid = threadIdx.x + blockDim.x * threadIdx.y; // must use column major order
int bid = blockIdx.x + gridDim.x * blockDim.y; // can either use column or row major
int offset = (blockDim.x * blockDim.y) * bid; // block id * threads per block
float vx = in_vector[tid + offset];

并且您将获得一次单独的warp读取共享内存中的唯一值。然后,其他线程可以从共享内存读取值,而无需任何进一步的全局内存访问。请注意,在上面的示例中,我遵循了代码的约定,即使以这种方式读取__shared__ float vx[32], vy[32]; int tid = threadIdx.x + blockDim.x * threadIdx.y; if (tid < 32) { vx[tid] = in_vector[blockIdx.x * blockDim.x + tid]; vy[tid] = in_vector[blockIdx.y * blockDim.y + tid]; } __syncthread(); 两次也不一定有意义。