__syncthreads()是否同步网格中的所有线程?

时间:2013-03-06 06:25:43

标签: cuda

...或者只是当前warp或block中的线程?

此外,当特定块中的线程遇到(在内核中)以下行

__shared__  float srdMem[128];

他们只会声明一次这个空间(每个块)吗?

它们显然都异步操作,所以如果块22中的线程23是到达该线的第一个线程,然后块22中的线程69是到达该线的最后一个线程,则线程69将知道它已经被声明?

5 个答案:

答案 0 :(得分:51)

__syncthreads()命令是块级同步障碍。这意味着当块中的所有线程到达屏障时使用它是安全的。也可以在条件代码中使用__syncthreads()但仅当所有线程都相同地评估此类代码时,否则执行可能会挂起或产生意外的副作用[4]

使用__syncthreads() :( source

的示例
__global__ void globFunction(int *arr, int N) 
{
    __shared__ int local_array[THREADS_PER_BLOCK];  //local block memory cache           
    int idx = blockIdx.x* blockDim.x+ threadIdx.x;

    //...calculate results
    local_array[threadIdx.x] = results;

    //synchronize the local threads writing to the local memory cache
    __syncthreads();

    // read the results of another thread in the current thread
    int val = local_array[(threadIdx.x + 1) % THREADS_PER_BLOCK];

    //write back the value to global memory
    arr[idx] = val;        
}

要同步网格中的所有线程,当前存在 not 本机API调用。在网格级别同步线程的一种方法是使用连续内核调用,因为此时所有线程都从同一点结束并重新开始。它通常也称为CPU同步或隐式同步。因此它们都是同步的。

使用此技术的示例(source):

CPU synchronization

关于第二个问题。 ,它确实声明了每个块指定的共享内存量。考虑到可用共享内存的数量是按 SM 衡量的。因此,应该非常小心 共享内存启动配置一起使用。

答案 1 :(得分:12)

__syncthreads()等待同一个块中的所有线程都到达命令并且warp中的所有线程 - 这意味着属于threadblock的所有warp必须到达该语句。

如果在内核中声明共享内存,则该数组仅对一个线程块可见。所以每个块都有自己的共享内存块。

答案 2 :(得分:9)

我同意这里的所有答案,但我认为我们在这里错过了一个重要的问题。第一个问题。我没有回答第二个答案,因为它在上面的答案中得到了完美的回答。

GPU上的执行以warp为单位。 warp是一组32个线程,并且在一次实例中,特定warp的每个线程执行相同的指令。如果在一个块中分配128个线程,则为GPU(128/32 =)4个warp。

现在问题变成"如果所有线程都在执行相同的指令,那么为什么需要同步?"。答案是我们需要同步属于 SAME 块的warp。 __syncthreads不会同步warp中的线程,它们已经同步。它同步属于同一块的warp。

这就是为什么回答你的问题的原因是:__syncthreads不同步网格中的所有线程,但属于一个块的线程作为每个块独立执行。

如果要同步网格,则将内核(K)分成两个内核(K1和K2)并同时调用它们。它们将被同步(K2将在K1结束后执行)。

答案 3 :(得分:3)

现有答案在回答turtle.tracer(0) # Turns off screen updates 的工作原理方面做得很好(它允许块内同步),我只是想添加一个更新,即现在有更新的方法>跨块同步。从CUDA 9.0开始,引入了“合作组”,它们允许同步整个块网格(如Cuda Programming Guide中所述)。这样可以实现与启动新内核相同的功能(如上所述),但是通常可以以较低的开销实现此目的,并使您的代码更具可读性。

答案 4 :(得分:1)

为了提供更多细节,除了答案之外,引用 seibert

更一般地说,__syncthreads() 是一种屏障原语,旨在保护您免受块内的先读后写内存竞争条件的影响。

使用规则非常简单:

  1. 当一个线程有可能读取另一个线程已写入的内存位置时,在写入之后和读取之前放置一个 __syncthreads()。

  2. __syncthreads() 只是块内的屏障,因此它无法保护您免受全局内存中的先写后读竞争条件的影响,除非唯一可能的冲突是同一块中的线程之间。 __syncthreads() 几乎总是用于保护写后读的共享内存。

  3. 不要在分支或循环中使用 __syncthreads() 调用,除非您确定每个线程都会到达相同的 __syncthreads() 调用。这有时可能需要您将 if 块分成几部分,以便将 __syncthread() 调用放在所有线程(包括那些未能通过 if 谓词的线程)将执行的顶层。

  4. 在循环中寻找写后读情况时,在确定将 __syncthread() 调用放在哪里时,它有助于在您的脑海中展开循环。例如,如果循环中的同一共享内存位置有来自不同线程的读取和写入,您通常需要在循环结束时额外调用 __syncthreads()。

  5. __syncthreads() 不标记临界区,所以不要那样使用它。

  6. 不要在内核调用的末尾放置 __syncthreads()。没有必要。

  7. 许多内核根本不需要 __syncthreads(),因为两个不同的线程永远不会访问同一个内存位置。