CUDA中的线程和块结构以及如何分配具有不同结构的线程(c ++)
Posted
技术标签:
【中文标题】CUDA中的线程和块结构以及如何分配具有不同结构的线程(c ++)【英文标题】:Thread and block structure in CUDA and how to allocate threads with different structures (c++) 【发布时间】:2021-06-05 21:01:13 【问题描述】:我刚刚开始学习 cuda 和并行编程,但学习起来很困难。如果我像这样声明线程块的大小和结构:
addKernel << < 1, 5 >> > (dev_c, dev_a, dev_b);
有人可以解释我将如何声明以下内容:
1)One 2D block
2)One 3D block
3)multiple 2D block in 1D block grid
4)multiple 2D block in 2D block grid
5)multiple 2D block in 3D block grid
6)multiple 3D block in 1D block grid
以及如何添加 2 个大小为 2^23-1 的较大数组大小。我不明白上述术语的含义以及如何在上面的 CUDA addKernel 行中表示它们。
const int arraySize = 2^23-1;
int a[arraySize] = 0 ;
int b[arraySize] = 0 ;
int c[arraySize] = 0 ;
上面我已经定义了数组大小并初始化了a、b和c。我正在尝试编写代码来并行添加 a 和 b 的内容并将结果存储在 c 中。我将如何以上述方式塑造线程结构并在线程之间拆分任务?谢谢你。我试图通过实现组织线程和块的不同方式来进一步理解线程层次结构。
【问题讨论】:
建议你缩小问题的范围,看起来你实际上是在要求一个关于 CUDA 的教程,并跟进一个特定的例子。 【参考方案1】:执行配置的左侧(<<< ... >>>
)为您提供网格维度(即网格中的块数),右侧是块维度(即块中的线程数)。请注意,运行时 API 内核启动语法中可能最多有四个参数,但最后两个与本讨论无关。
举几个例子:
在 x 和 y 方向分别有 2 个线程的单个 2D 块:<<< 1, dim3(2, 2) >>>
(注意 dim3
的默认值为 1)
单个 3D 块:<<< 1, dim3(2, 2, 2) >>>
一维网格中的多个二维块:<<< 4, dim3(2, 2) >>>
(“四个二维块”)
2D 网格中的多个 2D 块:<<< dim3(3, 3), dim3(2, 2) >>>
(“x 方向三个 2D 块,y 方向三个”)
3D 网格中的多个 2D 块:<<< dim3(3, 4, 5), dim3(2, 2) >>>
(“x-dir 中的三个 2D 块,y-dir 中的四个,z-dir 中的五个”)
1D 网格中的多个 3D 块:<<< 5, dim3(2, 2, 2) >>>
(“五个 3D 块”)
关于你的后一个问题(抱歉,这有点长):
通常,您只需要一个维度。 2D 和 3D 只是为了解决某些问题(例如 2D 用于卷积)。因此,如何选择尺寸取决于问题。
还请记住,每个块的线程数非常有限(x 和 y 维度通常为 1024……现在不确定 z,但关键是每个块不能有那么多)。
对于您的添加,您最终会执行以下操作:您决定每个块需要多少线程,例如 numThreads
(我认为建议使用 32 的倍数,例如 32 或 256),然后您计算块为ceil(n / numThreads)
,其中n
是数组的大小。这可确保您拥有的线程数至少与数组中的元素数一样多。
例如。如果你的数组有n = 1000
元素并且你决定去numThreadsPerBlock = 32
,你会得到ceil(1000 / 32) = ceil(31.25) = 32
块。这意味着您的内核将使用numBlocks * numThreadsPerBlock = 32 * 32 = 1024
线程启动。由于现在线程多于元素,因此您可能需要通过内核中的某些条件来检查这一点:if(idx < n) add()
。
在您的示例中,应该使用 1D 网格和 1D 块。您可能希望每个块有多个线程(例如 256 或 1024),否则您可能会超出设备的块限制,因为 n
在您的示例中非常大。
my_addition_kernel <<< ceil((2^23 - 1) / 1024), 1024 >>>(da, db, dc)
所以你会有 8192 个块,每个块有 1024 个线程。
(有时您无法一次将所有内容都放入设备中。然后您必须对此有所不同,例如,对子数据进行多次迭代。)
【讨论】:
感谢您的回复,它有很大帮助,我能问一下您在其他情况下如何将线程实际分配给这个问题,因为不幸的是它是任务的一部分。 int i = threadIdx.x;是我用于线程分配的代码。对于其他 6 种情况,这将如何改变?谢谢 我不会介绍所有这些,但是例如。对于 1D 网格中的多个 2D 块 我认为 你可以做<<< 8192, dim3(32, 32) >>>
和 2D 网格中的多个 2D 块:<<< dim3(4096, 2), dim3(32, 32) >>>
。您基本上是在拆分 1D 案例,但整体 #threads 保持不变。我在这里假设numThreadPerBlock = 1024
。我从来没有真正调用过这样的附加内核,我也没有仔细检查过。
int i = threadIdx.x
将是每个块的本地,并且您绝对不能在一个块中拥有多个 2^23
。要获取全局索引,请执行 int i = blockIdx.x * blockDim.x + threadIdx.x
。以上是关于CUDA中的线程和块结构以及如何分配具有不同结构的线程(c ++)的主要内容,如果未能解决你的问题,请参考以下文章