C++ 克服CUDA中的复制开销

C++ 克服CUDA中的复制开销,c++,optimization,cuda,unified-memory,C++,Optimization,Cuda,Unified Memory,我想使用CUDA在GPU上并行化一个图像操作,为图像的每个像素(或像素组)使用一个线程。操作非常简单:每个像素乘以一个值 但是,如果我理解正确,为了将图像放在GPU上并并行处理,我必须将其复制到统一内存或其他GPU可访问的内存中,这基本上是一个双for循环,就像在CPU上处理图像一样。我想知道是否有一种更有效的方法可以在GPU上复制图像(即1D或2D阵列),而这种方法没有开销,因此并行化是无用的 但是,如果我理解正确,为了将图像放在GPU上并并行处理,我必须将其复制到统一内存或其他GPU可访问的

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

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

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

你理解对了

我想知道是否有一种更有效的方法可以在GPU上复制没有开销的图像(即1D或2D阵列)

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

因此,并行化是无用的


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

操作非常简单


对于GPU加速带来的性能优势来说,这通常不是一个很好的指标。

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

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

对于一个非常简单的处理,只有读和写可以相互隐藏。简单计算没有隐藏任何其他内容的有意义的延迟。因此,通过Pipelinig,您可以将性能提高100%(假设输入1个映像,输出1个大小相同的映像,并且pcie/驱动程序在两个方向上执行相同的操作)

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

  • 将N行像素复制到vram
  • 计算N行并同时将N条新行复制到vram
  • 将N个结果复制回ram,(同时)计算N个新行,(同时/异步)将最新的N行复制到vram
  • 将最后的结果复制回ram
您可以为每个正在运行的N扫描线使用1个流(执行读+计算+写),并让驱动程序选择扫描线计算的最佳重叠,或者为每个操作类型使用1个流(1个用于所有写入,1个用于所有读取,1个用于所有计算),并使用事件显式维护重叠行为


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

没有一个操作…如果您只想执行一个图像并将每个像素乘以一个值,而图像由于某种原因尚未在GPU上,没有一个头脑正常的人会使用GPU来实现这一点(除了学习目的)。在性能开始变得有趣之前,您需要为GPU找到更多相关的工作。