【问题标题】:CUDA coalesced access for two-dimensional blockCUDA 对二维块的合并访问
【发布时间】:2012-09-09 10:55:08
【问题描述】:

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

但是我对二维情况有点卡住了(也就是说,我们有一个二维网格,由二维块组成)。

假设我有一个向量 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) 符号:

  • 第一个扭曲是:(0, 0), (1, 0), (2, 0), ..., (31, 0),
  • 第二个扭曲是:(0, 1), (1, 1), (2, 1), ..., (31, 1),
  • 等等……

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

我是否理解正确?如果是这样,我如何使用in_vector[j] 对全局内存进行合并访问?

【问题讨论】:

    标签: memory cuda


    【解决方案1】:

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

    int i = blockIdx.x * blockDim.x + threadIdx.x;
    float vx = in_vector[i];
    

    仅当 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();
    

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

    【讨论】:

    • 非常感谢您提供的信息丰富的回答。这正是我想要的。
    猜你喜欢
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 2019-08-08
    • 2012-05-06
    相关资源
    最近更新 更多