与 CUDA 中的线程和块并行化
Posted
技术标签:
【中文标题】与 CUDA 中的线程和块并行化【英文标题】:Parallelizing with threads and blocks in CUDA 【发布时间】:2019-05-13 02:54:15 【问题描述】:我有以下简单的嵌套 for 循环
float a[1024][1024], b[1024]
for(i=1; i < 1024; i++)
for(j = 1; j < 1024 - i; j++)
b[i + j] += a[i][j];
我正在尝试了解如何使用 CUDA 线程和线程块来分区此问题以与 GPU 并行化。到目前为止,我相信我总共进行了 N = 522753 次计算。我不完全确定如何从这里开始:我知道每个块中的线程数应该是 32 的倍数。例如,如果每个块的线程数是 1024,那么我至少需要 511 个块,每个线程从 1 -> N 进行计算。有人可以解释如何选择每个块的最佳线程数,以及如何实际并行实现。
【问题讨论】:
@talonmies 它来自提供给我的一个示例 - 并不意味着实际执行。比任何东西都多的伪代码,但我仍然不明白如何将每个块划分为块和线程以并行化概念 这种情况下的问题是不同的 (i,j)-pairs 想要写入同一个位置。说(3,0)和(0,3)。您可以使用 atomic_add,但最好以某种方式进行分区,即使用更少的 atomic_add 并且每个线程有更多的工作。 【参考方案1】:长评论:
编辑:c 矩阵应该是列主而不是行主,并且排序应该在列而不是行上,但为了便于阅读,我将其保留为行主。
您可以(一次)为每个工作项准备一个计数和引用矩阵,以便第一列是计数,其余是引用,最后一列是写入地址
c[0] = 1, &a[1][1], &b[2]; // b[2]
c[1] = 2, &a[1][2],&a[2][1], &b[3]; // b[3]
c[2] = 3, &a[1][3],&a[2][2],&a[3][1], &b[4]; // b[4]
..
然后根据它们的索引数量/子数组大小对它们进行排序(一次),使它们成为
c[0] = 1, &a[1][1], &b[2] // b[2]
c[1] = 1, &a[1022][1], &b[1023] // b[1023]
..
c[k] = 5, x1,y1,z1,t1,w1, &b[m] // b[m]
c[k+1] = 5, x2,y2,z2,t2,w2, &b[n] // b[n]
在 warp/block 的 cuda 线程之间平衡工作量。
然后访问 c 矩阵(每行 1 个 cuda 线程)以了解在每个工作项的普通 for 循环中添加哪些元素。
const int length = (int)c[workitemId][0];
for(int i=1;i<length+1;i++)
resultOfWorkitem += *(c[workitemId][i]);
*(c[workitemId][length+1])=resultOfWorkitem;
由于所有排序的列表只会排序一次,如果您要经常进行计算部分,那么这个额外的引用部分可能比使用原子更快,并且可能会为 c 和数组的只读访问而缓存。
如果随机写入地址成为性能问题,您可以根据最后一项的地址(连续的 b 索引)对 c 数组进行排序,但这会降低相邻 cuda 线程之间的工作平衡。也许这更快,没有测试。也许对 c 的第二个索引值进行排序可以通过减少读取次数来加快速度,尤其是当您对它们之间的每一行元素进行排序时,它们会与相邻线程的读取连续,类似于第一部分
c[0] = 1, &a[1][1] // address x \
c[1] = 2, &a[1][2] // address x+1 > less than L1 cache line size 128byte?
c[2] = 3, &a[1][3] // address x+2 /
保持每个工作项的连续地址访问和平衡工作是不可能的。
【讨论】:
以上是关于与 CUDA 中的线程和块并行化的主要内容,如果未能解决你的问题,请参考以下文章