我知道无法进行块同步,唯一的方法是启动新内核。
但是,假设我启动X块,其中X对应于我GPU上SM的数量。我应该注意调度程序会为每个SM分配一个块......对吗?如果将GPU用作辅助图形卡(完全专用于CUDA),这意味着理论上没有其他进程可以使用它......对吗?
我的想法如下:隐式同步。
让我们假设有时候我只需要一个块,有时我需要所有的X块。好吧,在我只需要一个块的情况下,我可以配置我的代码,以便第一个块(或第一个SM)可以处理“真实”数据,而其他X-1块(或SM)可以处理“虚拟“数据,执行完全相同的指令,只是一些其他偏移。
所有这些都将继续同步,直到我再次需要它们。
在这种情况下,调度程序是否可靠?或者你可以永远不确定吗?
答案 0 :(得分:3)
你有一个问题,所以我会尝试单独解决它们。
每个SM一个块
我在nVidia's own forums上回答了一段时间,因为我得到的结果表明这不是发生的事情。显然,如果块的数量等于SM的数量,则块调度程序不会为每个SM分配块。
隐式同步
没有。首先,您不能保证每个块都有自己的SM(见上文)。其次,所有块都无法同时访问全局存储。如果它们完全同步运行,它们将在第一次存储器读/写时失去同步性。
阻止同步
现在有好消息:是的,你可以。 CUDA C Programming Guide的B.11节中描述的原子指令可用于创建障碍。假设您在GPU上同时执行N
个阻止。
__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
并且仅由块中的第0个线程调用,即我们只想减少barrier
一次。指令atomicCAS(p,c,v)
设置*p = v
iff *p == c
并返回旧值*p
。这部分只是循环直到barrier
到达0
,即直到所有块都越过它。
请注意,您必须将此部分包装在对__synchtreads()
的调用中,因为块中的线程不会在严格的锁步中执行,您必须强制它们等待第0个线程。
请记住,如果您多次调用内核,则应将barrier
设置回N
。
<强>更新强>
在回复jHackTheRipper的答案和Cicada的评论时,我应该指出你不应该尝试启动比GPU上同时安排的更多块!这受到许多因素的限制,您应该使用CUDA Occupancy Calculator来查找内核和设备的最大块数。
从最初的问题来看,只有与SM一样多的区块正在启动,所以这一点没有实际意义。
答案 1 :(得分:-4)
@Pedro肯定是错的!
实现全球同步一直是最近几个研究工作的主题,最后是非Kepler架构(我还没有)。结论总是相同(或应该是):不可能在整个GPU上实现这样的全局同步。
原因很简单:CUDA块无法被抢占,因此,如果您完全占用GPU,则等待屏障rendesz-vous的线程将永远不会允许块终止。因此,它不会从SM中删除,并会阻止剩余的块运行。
因此,您将冻结永远无法摆脱此死锁状态的GPU。
- 编辑以回答佩德罗的评论 -
这些缺点已被其他作者注意到,例如: http://www.openclblog.com/2011/04/eureka.html
由OpenCL的作者在行动
- 编辑以回答佩德罗的第二句话 -
@Jared Hoberock在这篇SO帖子中得出了同样的结论: Inter-block barrier on CUDA