对于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)表示法:
在这种情况下,调用in_vector[i]
为我们提供了一个合并访问,因为同一warp中的每个连续线程都将访问连续的地址。但是调用in_vector[j]
似乎是一个错误的想法,因为每个连续的线程将访问全局内存中的相同地址(例如,warp 0中的所有线程都将访问in_vector [0],这将为我们提供32个不同的全局内存请求)
我是否理解正确?如果是这样,我如何使用in_vector[j]
?
答案 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();
两次也不一定有意义。