cuda,虚拟/隐式块同步

cuda,虚拟/隐式块同步,cuda,synchronization,Cuda,Synchronization,我知道块同步是不可能的,唯一的方法是启动一个新的内核 但是,让我们假设我启动了X块,其中X对应于我的GPU上的SM的数量。我应该知道调度程序将为每个SM分配一个块…对吗?如果GPU被用作辅助图形卡(完全专用于CUDA),这意味着,理论上,没有其他进程使用它。。。对吧? 我的想法如下:隐式同步 假设有时候我只需要一个块,有时候我需要所有的X块。好的,在那些我只需要一个块的情况下,我可以配置我的代码,这样第一个块(或第一个SM)将处理“真实”数据,而其他X-1块(或SMs)处理一些“虚拟”数据,执行

我知道块同步是不可能的,唯一的方法是启动一个新的内核

但是,让我们假设我启动了X块,其中X对应于我的GPU上的SM的数量。我应该知道调度程序将为每个SM分配一个块…对吗?如果GPU被用作辅助图形卡(完全专用于CUDA),这意味着,理论上,没有其他进程使用它。。。对吧?

我的想法如下:隐式同步

假设有时候我只需要一个块,有时候我需要所有的X块。好的,在那些我只需要一个块的情况下,我可以配置我的代码,这样第一个块(或第一个SM)将处理“真实”数据,而其他X-1块(或SMs)处理一些“虚拟”数据,执行完全相同的指令,只是使用一些其他偏移量

所以所有这些都将继续同步,直到我再次需要它们


在这种情况下,调度程序可靠吗?或者你永远也不能确定吗?

你有几个问题,所以我将尝试分别回答

每个SM一个区块

不久前我问过这个问题,因为我得到的结果表明这不是发生的事情。显然,如果块的数量等于SMs的数量,则块调度器不会为每个SM分配块

隐式同步

不可以。首先,您不能保证每个块都有自己的SM(见上文)。其次,所有块不能同时访问全局存储。如果它们完全同步运行,那么在第一次内存读/写时就会失去这种同步性

块同步

好消息是:是的,你可以。本手册第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
,并且仅由块中的第0个线程调用,即我们只想将
屏障减小一次。指令
atomicCAS(p,c,v)
设置
*p=v
iff
*p==c
,并返回
*p
的旧值。此部分仅循环,直到
障碍物
达到
0
,即直到所有块都穿过它

请注意,您必须在对
\uu synchtreads()
的调用中包装此部分,因为块中的线程不是在严格锁定步骤中执行的,您必须强制它们全部等待第0个线程

请记住,如果您多次调用内核,则应将
barrier
设置回
N

更新

在回答的回答和评论中,我应该指出,您不应该尝试启动超过GPU上可以同时调度的块数!这受到许多因素的限制,您应该使用查找内核和设备的最大块数


不过,从最初的问题来看,只有短信启动的街区数与短信启动的街区数一样多,所以这一点毫无意义。

@Pedro肯定是错的

实现全局同步是最近几项研究工作的主题,最后是非开普勒体系结构(我还没有)。结论总是一样的(或者应该是):在整个GPU上实现这样的全局同步是不可能的

原因很简单:CUDA块不能被抢占,因此如果您完全占用GPU,等待屏障rendez VOU的线程将永远不会允许该块终止。因此,它将不会从SM中移除,并将阻止其余块运行

因此,您将冻结永远无法摆脱此死锁状态的GPU

--编辑以回答佩德罗的评论--

其他作者也注意到了这些缺点,例如:

由OpenCL的作者在行动

--编辑以回答佩德罗的第二句话--

@Jared Hoberock在这篇SO帖子中得出了同样的结论:

@elect:是的,我实际上在自己的代码中使用了它,尽管没有调用
\uu syncthread()
,因为我每个块只有32个线程。如果你对我的话犹豫不决,你可能想看看“CUDA示例:通用GPU编程简介”的附录A,其中讨论了原子操作、块之间的互斥和同步。-1对不起,这是错误的!有关解释,请参阅查克瑟利普的答案。@djmj我知道,但我运行的算法必须运行数千个周期。在每一个循环中,我需要不同程度的并行化,也就是说,有时我只需要一个包含34个线程的块,有时需要N个包含N[1,34]的块(总是包含34个t)。关键是,在非WDDM系统上,每个内核调用的开销在3-20µs之间(他们说这要高得多)。现在我在win7上。然而,我希望尽快切换到linux,以获得如此低的开销。在任何情况下,完全避免它们都是很好的,也许只需要一个内核调用就可以了!:p@djmj只想更新一下WDDM系统中的内核开销。他们说不少于40µs(与3µs相比)。可能更高。你的建议是可能的,但很危险。看不,我不是“绝对错误”,否则这在我自己的代码中不会起作用。我已经添加了一个关于最大块数的注释,它解决了您对死锁的担忧。至于“几部研究作品”说这是不可能的,你能给我指出其中的一两部吗?这不是一个同时安排的问题,而是同时运行的blocksHow。你对同时运行的定义是什么?每个SM最多可以安排八个街区,这将