c++ GPU内存访问和使用(CUDA)

vyswwuz2  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(200)

我是CUDA的新手,对内存类型以及如何正确使用它们感到困惑。
我想做的很简单。我有一个二维数组data,其中N是4096。
i,j为当前元素。我需要访问它所有的邻居:(i + m, j + n),其中mn[-1, 0, 1]中的任一个。对于i = 0i-1变为N-1j = 0(周期性边界条件)也是如此。
1.我用这些邻居做了一些计算:

data[i][j] = data[i][j] + a * data[i+1][j] + b * data[i+1][j+1] + ...

1.然后,我需要等待所有其他N * N - 1线程完成这些计算并同步data数组。
1.我迭代了k次。

    • 我不确定是应该在内核内部还是外部迭代,因为我不知道它对内存有什么影响。

按照我的理解,我的内核应该是这样的:

__global__ void my_kernel(float* data, int rows, int cols)
{
    int i = (blockIdx.y * blockDim.y + threadIdx.y) % rows; 
    int j = (blockIdx.x * blockDim.x + threadIdx.x) % cols; 
    
    i = (i >= 0) ? i : rows + i;
    j = (j >= 0) ? j : cols + j;

    int t = i * sizeof(float) + j;
    
    for(int m = -1; m <= 1; m ++) 
    {
        for(int n = -1; n <= 1; n ++) 
        {
            if(m != 0 || n != 0) 
            {
                int s = (i + m) * sizeof(float) + (j + n);
                data[t] += data[s]/2;  // just an example 
            }
        }
    }   
}
    
...

int k = 1000;
int rows = 4096;
int cols = 4096;

dim3 block(8,8);
dim3 grid ( (cols + block.x -1)/block.x, (rows + block.y -1)/block.y );

for(int i = 0; i < k; i++) {
    my_kernel<<<grid, block>>>(reinterpret_cast<float*>(mat.data), rows, cols );
}

我不明白的是这里的记忆是怎么工作的。这是否意味着data数组会留在全局内存中,并在每个线程中从内核访问?据我所知,它相对较慢,我需要它尽可能快地计算。
同时,共享内存被限制为每个块48KB,data数组不适合,因为它的大小是64mb(4 * 4096 * 4096字节)。除此之外,它会将计算限制在单个块。
不过,我实际上并不需要在每个线程中使用整个data数组。我只需要9个元素。我的直觉告诉我,它应该在不访问全局记忆的情况下工作。这可能吗?有道理吗?如果是这样,我该怎么做?

nsc4cvqm

nsc4cvqm1#

是的,使用共享内存实现你想要的是可能的,是的,它应该加速你的代码,因为你的问题是内存限制的。
两年前,我成功地实现了这样的事情。为了弄清楚细节,我查看了矩阵乘法示例(其中使用了共享内存)。它可以在CUDA Samples文件夹中找到。
请注意,在您的情况下,主要区别在于相邻块应该重叠(1行或1列)(当您在每个块的边界上计算结果时,您将需要它)。实现是直接的,可以在一个内核调用中完成,其中您:
1)填满共享内存阵列。
2)__syncthreads();
3)使用共享内存阵列执行必要的计算。
选择block_size,以便共享阵列可以容纳每个块的共享内存。网格大小将是原始数组和block_size的比值

oewdyzsn

oewdyzsn2#

在3D中使用共享内存有一个所谓的模板模式的官方示例:5_Domain_Specific/FDTD3d。由于第三维是按顺序计算的,所以很容易将其缩减为2D。所以,是的,即使外部k循环将tile +“halo”层放入共享内存也是一个好主意,因为即使在单个k迭代中,多个线程也将访问相同的坐标i, j。尽管人们必须仔细地基准测试,因为缓存在GPU上变得越来越强大。我已经看到了3D模板代码,由于硬件的良好L1缓存,它们没有从共享内存中受益。
通常,在k迭代之间需要网格范围的同步。这通常是通过每次迭代启动一个内核来实现的(参见示例)。
利用今天来自协作组API的网格同步功能,可以在内核内部进行迭代,但由于协作启动的块数量有限,这将更难实现。这意味着每个块可能需要类似于3D循环顺序地计算多个瓦片(或对于共享存储器来说太大的瓦片),这将破坏在单次启动中进行计算的目的,因为数据不会被保存在共享存储器中。此外,也不能保证这实际上会比使用多次发射更快。
存在用于最小化块之间的同步/通信量的一些算法策略。它们通常应用于利用MPI的多节点HPC代码中(此处:MPI节点CUDA块)<->。例如,可以通过重叠瓦片(创建与外部“重影”或“晕圈”层不同的重复层)并且然后利用每个“内部迭代”缩小有效瓦片大小(阻止xlm 5 nlx迭代)来进行冗余计算。这样,在没有合作启动的情况下,可以在单个内核启动中实现多个但不是所有的k迭代。GPU上的这种优化的一个问题是块内的并行度随着内部迭代而下降。

相关问题