内核设计用于重叠数据,启动单独的warp

时间:2013-12-25 12:41:37

标签: cuda

我有一个关于CFD应用程序的问题,我试图根据我在网上找到的paper来实现。这可能是一个初学者的问题,但在这里:

情况如下: 2D域被分解为瓦片。这些图块中的每一个都由所讨论的内核块处理。正在执行的计算非常适合并行执行,因为它们只考虑了一小部分邻居(这是一个浅水应用程序)。瓷砖重叠。每个图块在域的每一侧都有2个额外的单元格,它应该计算结果。

block setup

在左侧,您会在右侧4看到1个区块,其中包含重叠区域。灰色是计算所需的“鬼细胞”。浅绿色是每个块实际上写回全局内存的域。不用说整个域将有超过4个瓦片。

每个主题的想法如下:

(1) copy data from global memory to shared memory
__synchthreads();
(2) perform some calculations
__synchthreads();
(3) perform some more calculations
(4) write back to globabl memory

对于绿色区域中的单元格,内核是直接的,您根据您的threadId复制数据,并使用共享内存中的邻居进行计算。由于数据依赖性的性质,这不足以满足:

  

(1)必须在所有细胞上运行(灰色和绿色)。没有依赖。

     

(2)必须在所有绿色单元格和灰色单元格的内部行/列上运行。取决于相邻数据N,S,E和W.

     

(3)必须在所有绿色细胞上运行。取决于来自步骤(2)的关于邻居N,S,E和W的数据。

所以这就是我的问题: 如果没有一个非常混乱的代码,怎么做呢?

所有我能想到的是一大堆“if”语句来决定一个线程是否应该执行这些步骤两次,具体取决于他的threadId。

我也考虑过使用重叠块(而不是仅重叠数据),但这会导致另一个问题:__synchthreads() - 调用必须在代码的条件部分。 分开内核并让步骤(2)/(3)在不同的内核中运行也不是一个选项,因为它们会产生中间结果,由于它们的数量/大小而不能全部写回内存。

作者自己写道(Brodtkorb et al.2010,GPU上的高效浅水模拟: 实施,可视化,验证和验证):

  

启动内核时,我们首先从全局内存读取到片上共享内存。除了块的内部单元,我们还需要使用每个方向上两个相邻单元的数据来完成数据   模板的依赖性。在将数据读入共享存储器之后,我们继续计算一维数据   分别在x和y方向上的通量。使用图1中所示的步骤,通过存储来计算通量   共享内存中多个线程使用的所有值。我们还在内部集体进行计算   一个块,以避免重复计算。但是,因为我们计算每个细胞的净贡献,我们有   执行比线程数更多的重建和通量计算,使我们的内核变得复杂。 这是   通过指定执行附加计算的单个warp来解决我们的代码;一个产生了一个战略的战略   比在几个warp之间划分额外的计算更好的性能。

那么,通过指定一个单独的warp进行这些计算是什么意思,以及如何做到这一点?

1 个答案:

答案 0 :(得分:2)

  

那么,通过指定一个单独的warp进行这些计算是什么意思,以及如何做到这一点?

你可以这样做:

  // work that is done by all threads in a block
  __syncthreads();  // may or may not be needed
  if (threadIdx.x < 32) {
    // work that is done only by the designated single warp
    }

虽然这很简单,但考虑到你问题中的最后一个问题,以及突出显示的段落,我认为这很可能是他们所指的。我认为这符合我在这里阅读的内容。此外,除了使用条件,我不知道将工作限制为单个warp的任何其他方法。他们也可能选择了一个warp来利用warp-synchronous行为,这种行为会在前面提到的条件代码问题中绕过__syncthreads();

  

所以这就是我的问题:如果没有一个非常混乱的代码,如何做到这一点?

     

所有我能想到的是一大堆“if”语句来决定一个线程是否应该执行这些步骤两次,具体取决于他的threadId。

实际上,我认为任何一系列普通的“if”陈述,无论多么混乱,都不能解决你所描述的问题。

解决您已经提到的步骤2和3之间的依赖关系的典型方法是将工作分成两个(或更多)内核。你表明这是“不是一个真正的选择”,但尽管我可以说,你正在寻找的是全局同步。除了内核启动/退出点之外,这样的概念在CUDA中没有明确定义。 CUDA不保证网格中块之间的执行顺序。如果步骤3中的块计算依赖于步骤2中的邻近块计算,那么在我看来,您肯定需要全局同步,如果您不实现它,您的代码将变得丑陋内核启动。在我看来,使用全局信号量或全局块计数器等替代方法很脆弱,难以应用于广泛数据依赖的一般情况(其中每个块都依赖于前一步骤的邻居计算)。

如果相邻计算仅依赖于来自一组相邻单元(“晕”)的数据,而不是整个相邻块,并且这些单元可以独立计算,则可能使您的块成为扩展到包括相邻单元格(即重叠),在相邻块之间有效地计算两次晕区,但是你已经表明你已经考虑并放弃了这个想法。但是,我个人希望在接受这样的想法之前详细考虑这些代码,这完全取决于__syncthreads();的困难,根据我的经验,那些说他们不能使用__syncthreads();的人条件代码执行没有准确地考虑所有选项,在详细代码级别,使__syncthreads();工作,即使在条件代码中也是如此。