【问题标题】:Overcoming the copy overhead in CUDA克服 CUDA 中的复制开销
【发布时间】:2021-08-13 08:52:45
【问题描述】:

我想使用 CUDA 在 GPU 上并行化图像操作,对图像的每个像素(或像素组)使用一个线程。操作非常简单:每个像素乘以一个值。

但是,如果我理解正确,为了将图像放在 GPU 上并使其并行处理,我必须将其复制到统一内存或其他一些 GPU 可访问的内存,这基本上是双 for 循环就像在 CPU 上处理图像的那个一样。我想知道是否有一种更有效的方法可以在 GPU 上复制图像(即 1D 或 2D 数组),并且不会产生并行化无用的开销。

【问题讨论】:

  • 没有...
  • 如果您想要执行的唯一操作是获取图像并将每个像素乘以一个值,并且由于某种原因图像尚未在 GPU 上,那么没有人会使用 GPU为此(可能出于学习目的除外)。在性能开始变得有趣之前,您需要找到更多涉及 GPU 的工作。

标签: c++ optimization cuda unified-memory


【解决方案1】:

但是,如果我理解正确,为了将图像放在 GPU 上并进行并行处理,我必须将其复制到统一内存或其他一些 GPU 可访问的内存

你没看错。

我想知道是否有更有效的方法在 GPU 上复制图像(即 1D 或 2D 数组)而没有开销

没有。主机系统内存中的数据必须通过 PCIE 总线才能到达 GPU 内存。这受 PCIE 总线带宽(PCIE Gen3 约为 12GB/s)的限制,并且还有一些与之相关的“固定开销”,至少每次传输大约几微秒,因此非常小的传输似乎更糟从性能(字节/秒)的角度来看。

这样并行化就没用了。

如果您想要执行的唯一操作是获取图像并将每个像素乘以一个值,并且由于某种原因该图像尚未在 GPU 上,那么他们头脑正常的人不会为此使用 GPU(除了可能用于学习目的)。在性能开始变得有趣之前,您需要找到更多涉及 GPU 的工作来完成

操作很简单

这通常不是 GPU 加速带来的性能优势的良好指标。

【讨论】:

    【解决方案2】:

    当您说“这基本上是一个双重 for 循环,就像在 CPU 上处理图像的循环一样”时,我希望您不是指在每行和每列上逐个像素地复制。您可以使用 memcpy 复制整个图像。但是,正如其他人所说,在 CPU 和 GPU 之间移动数据仍然存在相当大的开销,除非您在 GPU 上的计算足够复杂以证明开销是合理的。

    【讨论】:

      【解决方案3】:

      您可以隐藏一些复制延迟。在复制图像输入的补丁时,您可以同时从 GPU 上的先前计算中复制回结果补丁。在重叠的双向副本之上,可以运行第三个补丁的计算。这可以缩短单个图像处理或多个图像处理的总延迟(但这次隐藏了整个图像处理的延迟)。

      对于一个很简单的处理,只有读写可以相互隐藏。简单的计算没有有意义的延迟来隐藏其他任何东西。因此,通过流水线,您可以将性能提高 100%(假设输入 1 个图像,输出 1 个相同大小的图像,并且 pcie/driver 在两个方向上执行相同)。

      如果每个像素只是乘以一个值,那么它是令人尴尬的并行,您可以通过流水线处理任意大小的块来隐藏延迟。例如,

      • 将 N 行像素复制到 vram
      • 计算 N 行并同时将 N 个新行复制到 vram
      • 将 N 个结果复制回 ram,(并发)计算 N 个新行,(并发/异步)将最新的 N 个行复制到 vram
      • ...
      • 将最后一个结果复制回内存

      您可以为每个运行中的 N 条扫描线使用 1 个流(进行读取+计算+写入)并让驱动程序选择扫描线计算的最佳重叠,或者每种操作类型使用 1 个流(1 个用于所有写入, 1 表示所有读取,1 表示所有计算)并使用事件显式维护重叠行为。

      如果您对每个像素执行更多计算,例如等于复制的延迟,那么流水线将为您带来 3 倍的性能(隐藏在 1 后面的 2 个其他操作)。

      【讨论】:

        猜你喜欢
        • 2021-10-23
        • 2016-03-31
        • 1970-01-01
        • 2012-02-17
        • 1970-01-01
        • 2011-11-12
        • 2010-11-25
        • 2012-07-04
        • 2011-12-06
        相关资源
        最近更新 更多