我不相信你的内核能够做你想做的事情,因为while(t<tid) 中的分歧分支导致warp 的所有线程无限循环并且永远不会到达++t 行。
详细解释
如果您已经了解线程、块和扭曲,请滚动到“重要部分”以获取重要内容:
(我还没有使用 Kepler 架构的经验。如果不使用 Fermi,其中一些数字可能会有所不同。)
需要解释一些术语才能理解下一节:
以下术语与逻辑(软件构造中的逻辑)线程有关:
- thread – 单个执行线程。
- block – 一组执行相同内核的多个线程。
- 网格 - 一组块。
以下术语与物理(与硬件架构相关的物理)线程有关:
- 核心 - 单个计算核心,一个核心一次只运行一条指令。
- warp – 在硬件上并行执行的一组线程,一个 warp 由当前一代 CUDA 硬件上的 32 个线程组成。
内核由一个或多个流式多处理器 (SM) 执行。一个典型的
Fermi 系列的中高端 GeForce 显卡(GeForce 400 和 GeForce
500 系列)在单个 GPU 上有 8-16 个 SM[Fermi whitepaper]。每个 SM 由 32 个 CUDA Cores(核心)组成。线程由 warp 调度程序调度执行,每个 SM 都有
两个以锁步方式工作的 warp 调度器单元。最小的单位
一个warp调度器可以调度的称为warp,它由32个线程组成
撰写本文时迄今已发布的 CUDA 硬件。只有一个warp可以执行
在每个 SM 上一次。
CUDA 中的线程比 CPU 线程轻得多,上下文切换
更便宜,warp 的所有线程都执行相同的指令或必须
等待warp中的其他线程执行指令,这称为Sin-
gle 指令多线程 (SIMT),类似于传统的 CPU 单线程
指令多数据 (SIMD) 指令,例如 SSE、AVX、NEON、Al-
tivec 等,这在使用所描述的条件语句时会产生后果
再往下。
允许需要超过 32 个线程来解决 CUDA 的问题
线程被排列成称为 blocks 和 grids 的逻辑组,其大小分别为
由软件开发商定义。块是线程的 3 维集合,
块中的每个线程都有自己独立的 3 维标识号
ber 以允许开发人员区分内核代码中的线程。
单个块内的线程可以通过共享内存共享数据,这减少了
全局内存的负载。共享内存的延迟比全局低得多
内存,但资源有限,用户可以选择(每块)16 kB
共享内存和 48 kB L1 缓存或 48 kB 共享内存和 16 kB L1 缓存。
几个线程块可以依次组合成一个网格。网格是 3 维的
块数组。最大块大小与可用的硬件资源相关,而网格可以是(几乎)任意大小。网格内的块可以
仅通过全局内存共享数据,这是具有
最高延迟。
一个 Fermi GPU 可以有 48 个 warp(1536 个线程)在每个 SM 上一次激活,给定
线程使用足够少的本地和共享内存来同时适应所有
时间。线程之间的上下文切换很快,因为寄存器分配给
线程,因此不需要保存和恢复寄存器和共享
线程切换之间的内存。结果是实际上希望过度-
分配硬件,因为它会通过让
每当发生停顿时,warp 调度程序都会切换当前活动的 warp。
重要的部分
线程扭曲是在同一流式多处理器 (SM) 上执行的一组硬件线程。
warp 的线程可以比作在它们之间共享一个公共程序计数器
线程,因此所有线程必须执行同一行程序代码。如果
代码有一些分支语句,例如 if ... then ... else 经线必须
首先执行进入第一个块的线程,而其他线程
翘曲等待,接下来进入下一个块的线程将执行,而另一个
线程等待等等。由于这种行为,条件语句应该
尽可能避免在 GPU 代码中使用。当经线遵循不同的线时
执行它被称为具有不同的线程。 While 条件块
应该在 CUDA 内核中保持最低限度,有时可以
重新排序语句,以便同一经线的所有线程仅遵循一条路径
在if ... then ... else 块中执行并减轻此限制。
while 和for 语句是分支语句,因此不限于if。