GPU 内存访问和使用 (CUDA)
GPU memory access and usage (CUDA)
我是 CUDA 的新手,对内存类型以及如何正确使用它们感到非常困惑。
我想做的很简单。我有一个 NxN 元素的二维数组data
其中 N 是 4096。
让i,j
成为当前元素。我需要访问它的所有邻居:(i + m, j + n)
与m
和n
中的任何一个[-1, 0, 1]
. 对于i = 0
来说,i-1
变得N-1
。j = 0
也是如此(周期性边界条件)。
- 我使用这些邻居进行一些计算:
data[i][j] = data[i][j] + a * data[i+1][j] + b * data[i+1][j+1] + ...
-
然后我需要等到所有其他
N * N - 1
线程执行这些计算并同步data
数组。 -
我迭代了
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 40964096字节)。此外,它还会将计算限制为单个块。
不过,我实际上并不需要每个线程中的整个data
数组。我只需要 9 个元素。我的直觉说它应该在不访问全局内存的情况下工作。这可能吗?有意义吗?如果是这样,我该怎么做?
是的,可以使用共享内存实现您想要的内容,是的,它应该加速您的代码,因为您的问题是内存受限的。
两年前,我成功地实现了这样的东西。为了弄清楚细节,我查看了矩阵乘法示例(使用共享内存)。它可以在 CUDA 样本文件夹中找到。
请注意,在您的情况下,主要区别在于相邻的块应该重叠(1 行或 1 列)(当您在每个块的边界处计算结果时,您将需要它)。实现很简单,可以在1 个内核调用中完成,其中您可以:
1) 填满共享内存数组。
2)__syncthreads();
3) 使用共享内存数组执行必要的计算。
选择block_size
,以便共享阵列可以容纳在每个块的共享内存中。网格大小将是原始数组和block_size的比率
使用共享内存的这种所谓的 3D 模板图案有一个官方示例:5_Domain_Specific/FDTD3d
。由于第三维是按顺序计算的,因此将其简化为 2D 应该很容易。所以是的,即使使用外部k
循环,将瓦片+"光环"层放入共享内存也是一个好主意,因为即使在单次k
迭代期间,多个线程也将访问相同的坐标i, j
。尽管必须仔细进行基准测试,但缓存在 GPU 上变得越来越强大。我已经看到 3D 模板代码由于硬件的良好 L1 缓存而没有从共享内存中受益。
通常,需要在k
迭代之间进行网格范围的同步。这通常是通过每次迭代启动一次内核来实现的(请参阅示例)。
使用协作组 API 的当今网格同步功能,可以在内核内进行迭代,但由于用于协作启动的块数量有限,这将更难实现。这意味着每个块可能需要按顺序计算多个图块(或对于共享内存来说太大的图块),类似于 3D 循环,这将破坏在单次启动中进行计算的目的,因为数据不会保存在共享内存中。此外,不能保证这实际上比使用多次启动更快。
有一些算法策略可以最小化块之间的同步/通信量。它们通常应用于使用 MPI 的多节点 HPC 代码(此处:MPI 节点<-> CUDA 块)。例如,可以通过重叠瓷砖(创建与外部"重影"或"光晕"层不同的重复层)来进行冗余计算,然后在每次"内部迭代"(阻止k
迭代)时缩小有效图块大小。这样,可以在没有合作启动的情况下在单个内核启动中实现多个但不是全部k
迭代。在 GPU 上进行此优化的一个问题是,块内的并行度随着内部迭代而下降。
- CUDA:统一内存和指针地址的更改
- CUDA 使用共享内存平铺 3D 卷积实现
- 在统一内存 CUDA C/C++ 中分配 2D 向量
- CUDA非法访问内核内存
- 在尝试使用CUDA分配内存时,我遇到了访问冲突写入位置错误
- CUDA 数组如何存储在 GPU 内存中?它们在物理上是线性的吗?
- 越界访问 CUDA 共享内存
- 矩阵矢量产品 CUDA 通过平铺和共享内存提高性能
- CUDA - 统一内存(至少是帕斯卡)
- Mvapich在内核运行时在CUDA内存上僵局
- 可能的CUDA内存分配错误
- 是否有可能解决 CUDA 内存碎片问题
- CUDA内存分配性能
- CUDA 内存管理/类问题中的指针
- CUDA:内存限定符的非法组合
- 将矢量bool复制到CUDA内存时出错
- 内核中的"while"/"for"循环导致 CUDA 内存不足错误?
- CUDA内存错误
- 在内核调用(计算1.1或1.2)期间,仅用于设备计算的CUDA内存(类型)
- 大图像的 CUDA 内存分配问题