【问题标题】:Image computation on GPU and value returningGPU上的图像计算和值返回
【发布时间】:2018-08-10 12:35:42
【问题描述】:

我有一个 C# 项目,我在其中从相机中检索灰度图像并使用图像数据进行一些计算。这些计算非常耗时,因为我需要多次循环整个图像,而且我都是在 CPU 上完成的。

现在我想尝试在 GPU 上运行评估,但我很难做到这一点,因为我以前从未做过任何 GPU 计算。

该软件应该能够在具有不同硬件的多台计算机上运行,​​因此 CUDA 对我来说不是一个解决方案,因为代码也应该在只有板载显卡的笔记本电脑上运行。经过一番研究,我遇到了 Cloo (found it on this project),这似乎是一个相当合理的选择。

到目前为止,我将 Cloo 集成到我的项目中,并尝试让 this hello world 示例运行。我猜它正在运行,因为我没有收到任何异常,但我不知道在哪里可以看到打印输出。

对于我的计算,我需要将图像传递给 GPU,并且在计算过程中我还需要 x-y 坐标。因此,在 C# 中,计算如下所示:

int a = 0;
for (int y = 0; y < img_height; y++){
    for (int x = 0; x < img_width; x++){
        a += image[x,y] * x * y;
    }
}

int b = 0;
for (int y = 0; y < img_height; y++){
    for (int x = 0; x < img_width; x++){
        b += image[x,y] * (x-a) * y;
    }
}

现在我想让这些计算在 GPU 上运行,并且我想并行 y-loop,以便在每个任务中运行一个 x-loop。然后我可以在第二个循环块开始之前获取所有结果 a 值并将它们相加。

之后我想将值 ab 返回到我的 C# 代码并在那里使用它们。

所以,结束我的问题:

  1. 对于这项任务,Cloo 是一个值得推荐的选择吗?
  2. 将图像数据(16 位、短数组)和尺寸(img_widthimg_height)传递到 GPU 的最佳方式是什么?
  3. 如何从 GPU 返回值?据我所知,内核总是被用作kernel void...
  4. 实现循环的最佳方法是什么?

我希望我的问题很清楚,并且我提供了足够的信息来了解我的挣扎。任何帮助表示赞赏。提前致谢。

【问题讨论】:

  • 两个问题:(A) 您是否已经实现了引用的内核以至少掌握所质疑的领域? (B) 灰度 16 位色深图像 [x:0,?][y:0,?] 的静态尺寸是多少? O/P 的动机实际上会发生 2 倍吗? 20 倍? 200 倍?
  • 到目前为止,我只实现了hello world的内核,除此之外什么都没有。这些图像的大小为 1920x1200 像素,我需要在整个图像上进行两个循环,如代码示例中所示,这两个循环在另一个循环中运行大约 10 次。

标签: image-processing parallel-processing opencl gpu cloo


【解决方案1】:

让我们对问题进行逆向工程。了解image[][], image_height, image_width, a, b的“依赖链”的高效处理


Ad 4 ) 串联相同的for-loops 性能不佳

给定定义的代码,可能只有一个循环,因此可以降低开销成本,并且最好还最大化缓存对齐的矢量化代码。

Cache-Naive 重新制定:

int a = 0;
int c = 1;

for (     int  y = 0; y < img_height; y++ ){
    for ( int  x = 0; x < img_width;  x++ ){
          int      intermediate = image[x,y] * y; // .SET   PROD(i[x,y],y) 
          a += x * intermediate;                  // .REUSE 1st
          c -=     intermediate;                  // .REUSE 2nd
    }
}
int b = a * c; // was my fault upon being in a hurry leaving for weekend :o)

将代码移入拆分串联循环只会增加这些开销,并破坏代码性能调整中任何可能的缓存友好技巧。


Ad 3 + 2 ) 内核调用签名 + CPU 端方法允许这样做

OpenCL 和 Cloo 记录了这些细节,所以除了记录的方法之外,这里不需要任何神奇的东西。

然而,每个这样的主机端到设备端 + 设备端到主机端的传输都会产生延迟成本。鉴于您声称要在一个循环中重新处理 16 位 1920x1200 图像数据 ~10 次,这些延迟有可能不需要花费在每个这样的循环传递上。

最糟糕的性能杀手是非常浅的内核数学密度。问题是,内核中确实没有太多要计算的东西,所以任何高效的 SIMD / GPU 并行技巧的机会确实很低。

从这个意义上说,CPU 端智能矢量化代码将比 (H2D + D2H)-开销远延迟-敌对计算-浅 GPU-内核处理做得更好。


广告 1) 给定上面的 2+3 和 4,1 可能很容易失去意义

作为原型并提供额外的缓存友好矢量化技巧,内存中 + 缓存中矢量化代码将有机会击败所有 OpenCL 和混合 GPU/CPU 自动临时内核编译生成的设备代码及其计算工作.

【讨论】:

  • 感谢您的详细回答。一件事我还没有做到。如何将两个循环合二为一?因为b 的结果很大程度上取决于完整循环后a 的结果。在您的示例中,您还有一个未使用的int c。另外,为什么不将ab 也放入寄存器中呢?这些也被大量使用。非常感谢。
  • 好的,现在我明白了,测试了循环并得到了相同的结果。经过一夜的睡眠后,我可能会明白它为什么会起作用......还有一件事不起作用,@ 987654330@,这里它不知道关键字寄存器。我需要做其他事情才能使用register吗?
  • 按照这里的答案,register 似乎只在 C++ 中工作。在 C# 中,寄存器的分配由编译器完成。 stackoverflow.com/a/26186293/9228257
  • [register] 抱歉,设置编译器指令以最快地将新变量放入 CPU 寄存器的旧习惯,请跳过它,这是我的错。对于下一个级别,人们可能会受到彭博 IT 研究的启发,研究比默认更快的内存分配器,但这远远超出了草拟的解决方案方法。 [为什么工作] 处理只是更好地对齐并且(重新)使用依赖链的代数属性。这种依赖关系也说明了为什么 SIMD GPU 类型的问题重新表述没有太多空间,如上所示。
  • 我刚刚在我的代码中实现了循环的组合。计算现在只需要 20 毫秒而不是 120 毫秒,因此帮助很大。感谢您的帮助。
猜你喜欢
  • 2017-01-11
  • 2020-12-04
  • 1970-01-01
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
  • 2018-09-17
  • 2014-05-12
  • 2014-10-28
相关资源
最近更新 更多