Memory 二维块体的CUDA合并访问

Memory 二维块体的CUDA合并访问,memory,cuda,Memory,Cuda,对于1D情况,我已经非常了解CUDA中全局内存的整个联合访问需求 然而,我有点被二维的情况卡住了(也就是说,我们有一个二维网格,由二维块组成) 假设我在_vector中有一个向量,在我的内核中,我希望以合并的方式访问它。像这样: __global__ void my_kernel(float* out_matrix, float* in_vector, int size) { int i = blockIdx.x * blockDim.x + threadIdx.x; int j =

对于1D情况,我已经非常了解CUDA中全局内存的整个联合访问需求

然而,我有点被二维的情况卡住了(也就是说,我们有一个二维网格,由二维块组成)

假设我在_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)表示法:

  • 第一个经纱是:(0,0),(1,0),(2,0),…,(31,0)
  • 第二个经纱是:(0,1),(1,1),(2,1),…,(31,1)
  • 等等
在这种情况下,在_vector[i]
中调用
会给我们一个联合访问,因为同一个扭曲中的每个连续线程将访问连续地址。然而,在_vector[j]
中调用
似乎是一个坏主意,因为每个连续线程都将访问全局内存中的同一地址(例如,warp 0中的所有线程都将访问_vector[0],这将给我们32个不同的全局内存请求)


我理解对了吗?如果是这样的话,我如何使用_vector[j]
中的
对全局内存进行联合访问?

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

只有当
blockDim.x
大于或等于32时,才会从全局内存合并访问
in_vector
。即使在合并的情况下,共享相同
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];
如果您的目的不是读取每个线程的唯一值,那么您可以节省大量内存带宽,并使用共享内存实现聚合,如下所示:

__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();
\uuuuu共享\uuuuuuuuuuuvx[32],vy[32];
int tid=threadIdx.x+blockDim.x*threadIdx.y;
如果(tid<32){
vx[tid]=in_向量[blockIdx.x*blockDim.x+tid];
vy[tid]=in_向量[blockIdx.y*blockDim.y+tid];
}
__同步线程();

您将获得一次将唯一值读入共享内存的单个扭曲。然后,其他线程可以从共享内存中读取值,而不需要进一步的全局内存访问。请注意,在上面的示例中,我遵循了代码的约定,即使以这种方式两次读取
in_vector
并不一定有多大意义

非常感谢您提供了非常翔实的答案。这正是我要找的。
__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();