我是CUDA的新手,对内存类型以及如何正确使用它们感到困惑。
我想做的很简单。我有一个二维数组data
,其中N是4096。
设i,j
为当前元素。我需要访问它所有的邻居:(i + m, j + n)
,其中m
和n
是[-1, 0, 1]
中的任一个。对于i = 0
,i-1
变为N-1
。j = 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个元素。我的直觉告诉我,它应该在不访问全局记忆的情况下工作。这可能吗?有道理吗?如果是这样,我该怎么做?
2条答案
按热度按时间nsc4cvqm1#
是的,使用共享内存实现你想要的是可能的,是的,它应该加速你的代码,因为你的问题是内存限制的。
两年前,我成功地实现了这样的事情。为了弄清楚细节,我查看了矩阵乘法示例(其中使用了共享内存)。它可以在CUDA Samples文件夹中找到。
请注意,在您的情况下,主要区别在于相邻块应该重叠(1行或1列)(当您在每个块的边界上计算结果时,您将需要它)。实现是直接的,可以在一个内核调用中完成,其中您:
1)填满共享内存阵列。
2)
__syncthreads();
3)使用共享内存阵列执行必要的计算。
选择
block_size
,以便共享阵列可以容纳每个块的共享内存。网格大小将是原始数组和block_size的比值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上的这种优化的一个问题是块内的并行度随着内部迭代而下降。