CUDA:同一块中的线程同步

时间:2012-11-15 12:18:36

标签: cuda thread-synchronization

我尝试在CUDA中编写程序,但我在线程之间的同一块中遇到同步问题。

以下是模型情况:

 10 __global__ void gpu_test_sync()
 11 {
 12     __shared__ int t;
 13     int tid = threadIdx.x;
 14
 15     t = 0;
 16     __threadfence();
 17     __syncthreads();
 18
 19     // for(int i=0; i<1000000 && t<tid; i++); // with fuse
 20     while(t<tid);
 21
 22     t++;
 23     __threadfence();
 24 }
 25
 26 void f_cpu()
 27 {
 28     printf("TEST ... ");
 29     int blocks = 1;
 30     int threads = 2;
 31     gpu_test_sync<<< blocks , threads >>>();
 32     printf("OK\n");
 33 }

如果threads = 1,一切正常。如果线程&gt; 1,无限循环。

为什么呢?函数__threadfence();应该为其他线程提供t变量的可见值。

我如何解决?

3 个答案:

答案 0 :(得分:3)

我不相信你的内核能够做你想做的事情,因为while(t<tid)中的分支分歧导致warp的所有线程无限循环并且永远不会到达线{ {1}}。

长解释

滚动到&#39;重要部分&#39;如果您已经了解线程和块以及经线,那么重要的事情是:

(我对开普勒架构没有任何经验。如果不使用费米,这些数字中的一些可能会有所不同。)

需要解释一些术语才能理解下一部分: 以下术语与逻辑(软件构造中的逻辑)线程有关:

  • thread - 单个执行线程。
  • block - 一组执行相同内核的多个线程。
  • grid - 一组块。

以下术语与物理(依赖于硬件体系结构的物​​理)线程有关:

  • 核心 - 单个计算核心,一个核心一次只运行一条指令。
  • warp - 一组在硬件上并行执行的线程,warp由当前一代CUDA硬件上的32个线程组成。

内核由一个或多个流式多处理器(SM)执行。一个典型的 来自Fermi家族的中高端GeForce显卡(GeForce 400和GeForce 500系列)在单个GPU [Fermi whitepaper]上有8-16个SM。每个SM由32个CUDA核心(核心)组成。线程被调度由warp调度程序执行,每个SM都有 两个warp调度程序单元,以锁步方式工作。最小的单位 warp调度程序可以调度称为warp,它由32个线程组成 到目前为止,CUDA硬件在撰写本文时已发布。只能执行一次扭曲 在每个SM上。

CUDA中的线程比CPU线程,上下文切换更轻量级 更便宜,经线的所有线程都执行相同的指令或必须执行 等待warp中的其他线程执行指令时,这称为Sin- gle指令多线程(SIMT),类似于传统的CPU Single 指令多数据(SIMD)指令,如SSE,AVX,NEON,Al- tivec等,这在使用所描述的条件语句时会产生影响 进一步下来。

允许需要超过32个线程的问题来解决CUDA 线程被安排到称为网格的逻辑组中 由软件开发人员定义。块是三维线程集合, 块中的每个线程都有自己独立的三维标识号 允许开发人员区分内核代码中的线程。 单个块中的线程可以通过共享内存共享数据,这会减少 全局内存的负载。共享内存的延迟远低于全局内存 内存但是是有限的资源,用户可以选择(每块)16 kB 共享内存和48 kB L1缓存或48 kB共享内存和16 kB L1缓存。

依次可以将几个线程块分组为一个网格。网格是三维的 块数组。最大块大小与可用硬件资源相关联,而网格可以是(几乎)任意大小。网格中的块可以 仅通过全局内存共享数据,全局内存是GPU上的内存 最高延迟。

Fermi GPU可以为每个SM一次激活48个warp(1536个线程),给定 线程使用足够少的本地和共享内存来适应所有相同的内容 时间。由于寄存器被分配给线程,因此线程之间的上下文切换很快 线程,因此不需要保存和恢复寄存器和共享 线程切换之间的内存。结果是实际上希望超过 分配硬件,因为它会通过释放来隐藏内核中的内存停顿 每当发生停顿时,warp调度程序都会切换当前活动的warp。

重要部分

线程warp是在同一 Streaming Multiprocessor (SM)上执行的线程的硬件组。 可以比较warp的线程以在它们之间共享公共程序计数器 线程,因此所有线程必须执行相同的程序代码行。如果 代码有一些分支语句,例如warp必须的++t 首先执行进入第一个块的线程,而执行其他线程 warp等待,接下来进入下一个块的线程将执行另一个 线程等待等等。由于这种行为,条件语句应该 如果可能,请避免使用GPU代码。当经线的线程遵循不同的线条时 执行它被称为具有不同的线程。而条件块 应该在CUDA内核中保持最小,有时可以 重新排序语句,以便同一个warp的所有线程只跟随一个路径 在if ... then ... else块中执行并减轻此限制。

if ... then ... elsewhile语句是分支语句,因此不限于for

答案 1 :(得分:2)

当您使用多个线程启动内核时,您有一个无限循环,因为while(t<tid);idx大于零的任何线程的无限循环。

此时,您的问题与线程同步无关,而与您实施的循环无关。

答案 2 :(得分:1)

如果你要做的是获得一系列线程来执行,那么你就是在滥用CUDA。

它也无法工作,因为任何超过第一个的线程都不会收到更新的t - 你必须调用__syncthreads()来刷新共享变量,但是只有在所有线程都在执行时才能这样做同样的事情 - 即不等待。