【问题标题】:CUDA shared memory - sum reduction from kernelCUDA 共享内存 - 从内核减少总和
【发布时间】:2013-09-22 12:15:11
【问题描述】:

我正在处理图像立方体 (450x450x1500) 的大型数据集。我有一个适用于单个数据元素的内核。每个数据元素产生 6 个中间结果(浮点数)。我的块由 1024 个线程组成。每个线程(6 个浮点数组)将 6 个中间结果存储在共享内存中。但是,现在我需要添加每个中间结果以产生总和(6 个总和值)。我没有足够的全局内存来将这 6 个浮点数组保存到全局内存中,然后从主机代码中运行推力或任何其他库的缩减。

是否有任何可以从共享内存中数组的内核函数内部调用的归约例程?

解决此问题的最佳方法是什么?我是 CUDA 编程的新手,欢迎提出任何建议。

【问题讨论】:

  • “6 个总和值”是指最终结果仅包含 6 个浮点数,还是仅对 6 个浮点数进行 450x450x1500 次的缩减?
  • 最终结果包含 6 个浮点数。总和超过第三维 (1500)。所以我最终需要得到 450x450x6 的浮点数。
  • 那么你的图像立方体是如何存储在全局内存中的?一帧一帧像image[1500][450][450] 或一个像素一个像素像image[450][450][1500]
  • 它首先沿着 z,然后沿着 x 和 y。所以图像[1500] [450] [450]。我有一个线程处理每个体素。由于一个块不能有 1500 个线程,我每个块使用 512 个线程并将 1500 个线程分成三个块。我最终需要从所有三个块中累积结果。我在想我将使用临时全局内存(450x450x6)来保存每个块的中间总和值。这是一个好方法吗?
  • 您不需要使用多个块或多个线程对一个 1500 维向量求和。对于您的情况,使用一个线程就足够了。请参阅我的更新答案。

标签: cuda


【解决方案1】:

这似乎不太可能:

我没有足够的全局内存来将这 6 个浮点数组保存到全局内存中,然后从主机代码中运行推力或任何其他库的缩减。

我无法想象您如何有足够的空间将数据存储在共享内存中而不是全局内存中。

无论如何,CUB 提供了可以从线程块内调用的缩减例程,并且可以对存储在共享内存中的数据进行操作。

或者您可以编写自己的减和代码。做起来并不难,关于SO的问题很多,比如this one

或者你可以调整cuda sample code

【讨论】:

  • 问题是我已经在设备上分配了大量的卷。我正在使用具有 3 GB 内存的 K4000。需要为每个体素计算需要相加的浮点数。例如(我之前的问题中的一个较小的卷),如果我的卷大小是 300x300x1500,如果我必须将中间值保存到全局内存,我需要 300x300x1500x6x4 = ~ 3.01 GB。我的理解是,由于处理不是一次全部安排的,因此共享内存能够保存它,因为任何时候只有部分数据需要在真实内存中。
  • 是的,那么在处理数据时保留总和可能是有意义的。我提到的 3 种方法中的任何一种都可以以这种方式使用。
  • 感谢您的建议。我根据 Mark Harris 的解释实现了并行缩减。但是现在我有一个新问题。正如我在回复 Eric 的 cmets 中提到的,我必须减少 1500 个元素。但由于块的最大限制,我将数据分成 3 段,每段 512。我有每个块的 3(3x6) 结果。我正在将块中的结果写入全局内存并进行累积。但是每次都会得到不同的结果,因为块的执行顺序不是恒定的。我可以跨块同步吗?
  • 没有内置的跨块同步方法。同样的问题出现在标准约简的例子中,所以如果你研究它,你会找到解决这个问题的方法。您可以使用atomic 函数来处理对全局内存的写入/更新(听起来每个块只写入几个值,所以这不应该是一个巨大的性能问题)或将经典的并行缩减扩展到多内核方法。还有其他可能性。也许提出一个新的 SO 问题。
【解决方案2】:

更新

看了所有的 cmets 后,我明白了,你需要做 450x450x6 次的缩减,而不是做 1 次或几次缩减。

在这种情况下,有更简单的解决方案。

您不需要为每个 1500-D 向量实现相对复杂的并行归约。由于您已经有 450x450x6 向量要归约,您可以使用传统的串行归约方法并行归约所有这些向量。

您可以使用具有 16x16 线程的块来处理图像的特定区域,并使用具有 29x29 块的网格来覆盖整个 450x450 图像。

在每个线程中,您可以遍历 1500 帧。在每次迭代中,您可以先计算 6 个中间结果,然后将它们加到总和中。当你完成所有迭代时,你可以将 6 个和写入全局内存。

这样就完成了内核设计。并且不需要共享内存。

您会发现性能非常好。由于它是一个内存绑定操作,它不会比简单地访问一次所有图像立方体数据更长。

如果您没有足够的全局内存用于整个多维数据集,您可以将其拆分为 [1500][225][225] 的 4 个子多维数据集,并在每个子多维数据集上调用内核例程。您唯一需要更改的是网格大小。

【讨论】:

  • 嗨,Eric,我没有 450x450x1500x6 的值开始。我必须首先从每个体素中计算出我必须求和的值。此外,由于数据量很大,我什至无法将 450x450x1500x6(浮点数)保存到全局内存中以便稍后进行处理。
  • 嗨 Eric,由于必须从每个像素计算中间值,因此仅使用 450x450 线程似乎不是最优的。在这种情况下,每个线程必须从全局内存中读取 1500 个值,然后对每个像素进行相同的计算。这不是最好的并行化吗?
  • @user2789280 我的方法不会将 450x450x1500x6 浮点数存储到全局内存中。中间结果将在计算出来后立即添加到 sum 中。 450x450 线程,即 29*29 块 256-thread-block 可以满足 GPU 的所有 SM。事实上,对于 CC2.0 设备,只有 32 个这样的模块能够做到这一点。
【解决方案3】:

查看this,它彻底解释了 CUDA 的并行缩减。

【讨论】:

  • 这是文档的旧版本,可在我的 cuda 示例代码链接中找到。
【解决方案4】:

如果我理解正确,每个线程应该总结“仅”6 浮动。

我不确定总体上是否值得通过并行减少来执行此操作,因为您将体验到性能提升。

如果您以 Kepler 为目标,您可以尝试使用 shuffle 操作,前提是您正确设置了块大小,以便您的中间结果以某种方式适合流式多处理器的寄存器。

正如 Robert Crovella 也指出的那样,您关于存储中间结果的可能性的说法似乎很奇怪,因为全局内存的数量肯定大于共享内存的数量。

【讨论】:

  • 请看我对前两个回复的cmets。减少超过 1500 个元素,由于数据量大,空间有限。
猜你喜欢
  • 2016-06-08
  • 1970-01-01
  • 2011-06-29
  • 2012-05-04
  • 2014-10-01
  • 2021-01-19
  • 2020-11-27
  • 1970-01-01
  • 2019-06-03
相关资源
最近更新 更多