您有几个问题合二为一,所以我将尝试分别解决。
每个 SM 一个块
我在nVidia 自己的论坛上问过这个问题,因为我得到的结果表明这不是发生的情况。显然,如果块的数量等于 SM 的数量,则块调度器不会为每个 SM 分配一个块。
隐式同步
不,首先,你不能保证每个区块都有自己的 SM(见上文)。其次,所有块不能同时访问全局存储。如果它们完全同步运行,它们将在第一次内存读/写时失去这种同步性。
块同步
现在有个好消息:是的,你可以。CUDA C 编程指南第 B.11 节中描述的原子指令可用于创建屏障。假设您有N
块在 GPU 上同时执行。
__device__ int barrier = N;
__global__ void mykernel ( ) {
/* Do whatever it is that this block does. */
...
/* Make sure all threads in this block are actually here. */
__syncthreads();
/* Once we're done, decrease the value of the barrier. */
if ( threadIdx.x == 0 )
atomicSub( &barrier , 1 );
/* Now wait for the barrier to be zero. */
if ( threadIdx.x == 0 )
while ( atomicCAS( &barrier , 0 , 0 ) != 0 );
/* Make sure everybody has waited for the barrier. */
__syncthreads();
/* Carry on with whatever else you wanted to do. */
...
}
该指令以原子方式atomicSub(p,i)
计算*p -= i
并且仅由块中的第零个线程调用,即我们只想递减barrier
一次。该指令atomicCAS(p,c,v)
设置*p = v
iff*p == c
并返回 的旧值*p
。这部分只是循环直到barrier
到达0
,即直到所有块都穿过它。
请注意,您必须将此部分包装在调用中,__synchtreads()
因为块中的线程不会以严格的锁步执行,您必须强制它们全部等待第零个线程。
请记住,如果您多次调用内核,则应设置barrier
回N
.
更新
在回复jHackTheRipper的回答和Cicada的评论时,我应该指出,您不应该尝试启动比 GPU 上可以同时安排的更多的块!这受到许多因素的限制,您应该使用CUDA 占用计算器来查找内核和设备的最大块数。
不过,从最初的问题来看,只有与 SM 一样多的区块正在启动,所以这一点没有实际意义。