【问题标题】:Basic GPU application, integer calculations基本 GPU 应用,整数计算
【发布时间】:2015-05-20 23:32:10
【问题描述】:

长话短说,我已经做了几个交互软件的原型。我现在使用 pygame(python sdl 包装器),一切都在 CPU 上完成。我现在开始将它移植到 C 中,同时寻找现有的可能性来使用一些 GPU 功能来使 CPU 摆脱冗余操作。但是,我找不到一个好的“指南”,我应该在我的情况下选择哪些确切的技术/工具。我刚刚阅读了大量的文档,它很快耗尽了我的精神力量。我不确定这是否可能,所以我很困惑。
在这里,我对我开发的典型应用程序骨架做了一个非常粗略的草图,但考虑到它现在使用 GPU(注意,我对 GPU 编程的实践知识几乎为零)。仍然重要的是必须准确保留数据类型和功能。这里是:

所以 F(A,R,P) 是一些自定义函数,例如元素替换、重复等。函数在程序生命周期中可能是恒定的,矩形的形状一般不等于 A 形状,所以它不在-地方计算。所以它们只是用我的函数生成的。 F的例子:重复A的行和列;用替换表中的值替换值;将一些图块组合成单个数组; A 值上的任何数学函数等。如上所述,所有这些都可以在 CPU 上轻松完成,但应用程序必须非常流畅。顺便说一句,在纯 Python 中,在添加了几个基于 numpy 数组的视觉功能后,它变得无法使用。 Cython 有助于制作快速的自定义函数,但源代码已经有点像沙拉了。

问题:

  • 此架构是否反映了某些(标准)技术/dev.tools?

  • CUDA 是我想要的吗?如果是,一些与我的应用程序结构一致的链接/示例会很棒。

我知道,这是个大问题,所以如果有帮助,我会提供更多细节。


更新

这是我的位图编辑器原型的两个典型计算的具体示例。因此编辑器使用索引,并且数据包括具有相应位掩码的图层。我可以确定图层的大小和蒙版的大小与图层的大小相同,例如,所有图层的大小都相同(1024^2 像素 = 4 MB,对于 32 位值)。我的调色板是 1024 个元素(32 bpp 格式为 4 KB)。
考虑一下我现在想做两件事:

第 1 步。我想将所有图层合二为一。假设 A1 是默认图层(背景),图层“A2”和“A3”具有蒙版“m2”和“m3”。在python中我会写:

from numpy import logical_not
...
Result = (A1 * logical_not(m2) + A2 * m2) * logical_not(m3) + A3 * m3

由于数据是独立的,我相信它必须与并行块的数量成正比。

第 2 步。现在我有一个数组,想用一些调色板“着色”它,所以它将是我的查找表。正如我现在所看到的,同时读取查找表元素存在问题。

但我的想法是,也许可以为所有块复制调色板,这样每个块都可以读取自己的调色板?像这样:

【问题讨论】:

  • 仅供参考:有 Theano (Python) 可以使用 GPU。它将符号表达式编译为在 GPU 上运行的 CUDA 代码。
  • 如果有很多独立的数据通过它(数百 MB/s),那么它真的可以加快速度。我看到的唯一问题是“生成的矩形可以是任意大小”。我从来没有听说过/读过关于通过内核分配的内容。如果在启动之前知道它的大小,那没关系,但是必须在 GPU 内核启动之前定义数组。所以它基本上取决于F(输入和输出)和A的大小
  • @blind.wolf 是的,大小是已知的,只是不能与 A 的形状相同,例如在整数数组放大的情况下。
  • 为什么会有人投票结束这个问题?????
  • @buttifulbuttefly 好吧,我想是的。例如,假设您有几个数组,并且您想获得一个平均值。类似于 Photoshop 中的图层,但只有索引值。现在移动一层并实时查看变化的结果。但首先,我想要smth。非常非常简单,就像在提到的带有替换或行/列重复的示例中一样。如果我可以让我的数组充满调色板值,那将是一个很好的改进。替换可以完美并行,对吗?

标签: c gpu gpgpu


【解决方案1】:

当您的代码高度并行(即处理阶段之间的数据依赖性很小或没有数据依赖性)时,您可以选择 CUDA(对同步进行更细粒度的控制)或 OpenCL(非常相似且可移植的类似 OpenGL 的 API 与用于内核处理的 GPU)。我们所做的大部分加速工作都发生在 OpenCL 中,它与 OpenGL 和 DirectX 具有出色的互操作性,但我们也有与 CUDA 相同的设置。 CUDA 和 OpenCL 之间的一大区别是,在 CUDA 中,您可以编译内核一次并在应用程序中延迟加载(和/或链接)它们,而在 OpenCL 中,编译器与 OpenCL 驱动程序堆栈配合得很好,以确保内核在应用程序启动。

如果您使用 Microsoft Visual Studio,则经常被忽视的一个替代方案是 C++AMP,它是一种 C++ 语法友好且直观的 API,适用于那些不想深入研究 OpenCL/CUDA API 的逻辑曲折的人.这里最大的优势是,如果系统中没有 GPU,代码也可以工作,但是你没有那么多选项来调整性能。尽管如此,在很多情况下,这是一种快速有效的方式来编写证明您的概念代码并稍后在 CUDA 或 OpenCL 中重新实现位和部分。

只有当您遇到同步问题和大量数据依赖项时,OpenMP 和线程构建块才是不错的选择。使用工作线程的本机线程也是一种可行的解决方案,但前提是您对如何在不同进程之间设置同步点有一个很好的了解,这样线程在争夺优先级时不会互相饿死。要做到这一点要困难得多,并且必须使用 Parallel Studio 等工具。但是,如果您正在编写 GPU 代码,那么 NVida NSight 也是如此。

附录:

一个名为 Quasar (http://quasar.ugent.be/blog/) 的新平台正在开发中,它使您能够以与 Matlab 非常相似的语法编写数学问题,但完全支持 c/c++/c# 或 java 集成,并且可以跨- 将您的“内核”代码编译(LLVM、CLANG)到任何底层硬件配置。它生成 CUDA ptx 文件,或在 openCL 上运行,甚至在您的 CPU 上使用 TBB 或它们的混合物。使用一些名字,您可以修饰算法,以便底层编译器可以推断类型(您也可以显式使用严格类型),因此您可以将类型繁重的东西完全留给编译器。公平地说,在撰写本文时,该系统仍处于 w.i.p.第一个 OpenCL 编译程序正在测试中,但最重要的好处是快速原型设计,与优化的 cuda 相比具有几乎相同的性能。

【讨论】:

  • 感谢您的信息。然而,我的主要问题更多的是表面上。我只需要几个循环来扫描键盘状态和调用函数。现在我用 Python 编写(使用 VIM)。到目前为止,我可以在不调试的情况下做一些事情。移植到 C/C++(我什至几乎不需要 C++ 来处理这类应用程序)可能会解决性能问题,它仍然是标准方法,其他工具也支持。简而言之,如何将普通的 C 程序(比如 SDL)与并行代码结合起来?
  • 在我回答之前,请注意线程和并行编程之间存在区别,因为线程是关于同步和调度作业,而 GPU 并行编程是组织代码和数据的意识形态方法在更广泛的意义上,它适合并行硬件执行。说白了,第一种并行显式地处理复杂的数据依赖关系,这是 GPU 并行根本不喜欢的。根据您的描述,听起来您需要 pthreads,例如:参见 softpixel.com/~cwright/programming/threads/threads.c.php
  • 不,现在我不想要 CPU 并行化或 CPU 端的线程。我的意思是,我有一个主循环(对不起,我之前写过“循环”),所以我扫描键盘状态然后做一些数学运算,但结果,即标准化的数组,想要用 GPU 处理。所以我想我只需要从我的程序中调用一些“特殊”函数。仍然没有太大的成功找到明确的指示。它只是典型的实时交互应用程序。我也不需要来自 GPU 的反馈。
  • CUDA 和 OpenCL 库本质上都是 C 库,因此所有教程都应该简单地适用于 C 语言而不会有太多麻烦。
  • 是的,很清楚。然而,“所有教程”对我来说是个问题。有几次从 Cuda 文档和其他一些文档开始,但它把我带到了其他地方,而不是我想要的。我认为讨论教程是题外话,这就是为什么我询问是否有人可以分享简单的相关示例。我现在试试fixstars.com/en/opencl/book,很好理解。
【解决方案2】:

您要做的是使用高频分派非常快速地将值发送到 GPU,然后显示一个函数的结果,该函数基本上是纹理查找和一些参数。

我想说这个问题只有在满足两个条件的情况下才值得在 GPU 上解决:

  1. A[] 的大小经过优化,使传输时间变得无关紧要(看,http://blog.theincredibleholk.org/blog/2012/11/29/a-look-at-gpu-memory-transfer/)。

  2. 查找表不是太大和/或查找值的组织方式可以最大限度地利用缓存,一般而言,GPU 上的随机查找可能会很慢,理想情况下您可以预加载A[] 缓冲区的每个元素在共享内存缓冲区中的 R[] 值。

如果您当时可以肯定地回答这两个问题,然后才考虑尝试使用 GPU 来解决您的问题,否则这两个因素将压倒 GPU 可以为您提供的计算速度。

您可以看看的另一件事是尽可能重叠传输和计算时间,以尽可能隐藏 CPU->GPU 数据的缓慢传输速率。

关于您的F(A, R, P) 函数,您需要确保您不需要知道F(A, R, P)[0] 的值才能知道F(A, R, P)[1] 的值是什么,因为如果您知道,那么您需要重写@987654329 @ 使用一些并行化技术来解决这个问题。如果您的 F() 函数数量有限,则可以通过编写每个 F() 函数的并行版本供 GPU 使用来解决此问题,但如果 F() 是用户定义的,那么您的问题就会变得有点棘手。

我希望这些信息足以对您是否应该使用 GPU 来解决您的问题做出明智的猜测。

编辑

阅读您的编辑后,我会说是的。调色板可以放入共享内存(请参阅GPU shared memory size is very small - what can I do about it?),这非常快,如果您有多个调色板,则可以容纳 16KB(大多数卡上共享内存的大小)/每个调色板 4KB = 每个线程块 4 个调色板.

最后一个警告,整数运算在 GPU 上并不是最快的,如果需要,请在实现算法后考虑使用浮点数,并且它可以作为一种廉价的优化。

【讨论】:

  • 非常感谢,这为我的问题提供了一点启示。我进行了更新,说明了确切的示例。理论上它回答了标准,但我不确定,特别是关于这个查找表缓存。你能看看并说它是否会像我想象的那样工作吗?因为这两个计算实际上对于应用来说是最典型的。
  • 谢谢,这是我目前需要的信息。新的千年位图编辑器即将推出 :)
【解决方案3】:

OpenCL/CUDA 之间没有太大区别,因此请选择更适合您的。请记住,CUDA 将限制您使用 NVidia GPU。

如果我正确理解您的问题,内核(在 GPU 上执行的函数)应该很简单。它应该遵循这个伪代码:

kernel main(shared A, shared outA, const struct R, const struct P, const int maxOut, const int sizeA)
  int index := getIndex() // get offset in input array
  if(sizeA >= index) return // GPU often works better when n of threads is 2^n
  int outIndex := index*maxOut // to get offset in output array
  outA[outIndex] := F(A[index], R, P)
end

函数 F 应该是内联的,你可以使用 switch 或 if 来实现不同的函数。由于 F 的输出大小未知,因此您必须使用更多内存。每个内核实例必须知道正确的内存写入和读取的位置,因此必须有一些最大大小(如果没有,那么这一切都是无用的,您必须使用 CPU!)。如果不同的大小是稀疏的,那么我会在将数组返回 RAM 并用 CPU 计算这几个后使用类似计算这些不同大小的方法,同时用一些零或指示值填充 A。

数组的大小显然是 length(A) * maxOut = length(outA)。

我忘了提到,如果在大多数情况下(相同的源代码)F 的执行不一样,那么 GPU 会序列化它。 GPU 多处理器有几个核心连接到同一个指令缓存中,因此它必须对代码进行序列化,这对所有核心都不一样!对于此类问题,OpenMP 或线程是更好的选择!

【讨论】:

  • 好的,所以你的意思是完全有可能做这样的事情。然后我会尝试使用 OpenCL,我很感兴趣。如果有有趣的事情出现,我会更新。
  • 应该是,但是我不确定真正的加速。如果它真的遵循这种模式,那么加速应该是可观的。问题在于 F。如果它的执行真的是随机的,那么它将大部分被序列化,最好使用 OpenMP 或线程来执行,因为 GPU 将序列化大部分代码。 (使用序列化代码,CPU 至少快 3 倍)
猜你喜欢
  • 1970-01-01
  • 2011-05-20
  • 2014-09-12
  • 2019-08-06
  • 2012-06-04
  • 2012-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多