你有几个问题,所以我会尝试分别解决。
每个 SM 一个区块
我在nVidia's own forums 上问过这个问题,因为我得到的结果表明这不是发生的情况。显然,如果块的数量等于 SM 的数量,则块调度器不会为每个 SM 分配一个块。
隐式同步
没有。首先,你不能保证每个区块都有自己的 SM(见上文)。其次,所有块不能同时访问全局存储。如果它们完全同步运行,它们将在第一次内存读/写时失去这种同步性。
块同步
现在有个好消息:是的,你可以。 CUDA C Programming Guide 的 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 Occupancy Calculator 来查找内核和设备的最大块数。
不过,从最初的问题来看,只有与 SM 一样多的区块正在启动,所以这一点没有实际意义。