基本GPU应用程序,整数计算

时间:2015-05-20 23:32:11

标签: c gpu gpgpu

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

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

问题:

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

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

我意识到,这是一个很大的问题,所以如果有帮助,我会提供更多细节。

更新

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

第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步。现在我有一个数组,并希望用一些调色板“着色”它,所以它将是我的查找表。正如我现在看到的,查找表元素的同时读取存在问题。  enter image description here

但我的想法是,可能只需复制所有块的调色板,因此每个块都可以读取自己的调色板?像这样: enter image description here

3 个答案:

答案 0 :(得分:2)

当您的代码高度并行时(即处理阶段之间存在很小的数据依赖性或没有数据依赖性),那么您可以使用CUDA(对同步更精细的控制)或OpenCL(非常类似的AND便携式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上运行,甚至在使用TBB或其混合的CPU上运行。使用一些标记,您可以修改算法,以便底层编译器可以推断类型(您也可以显式使用严格类型),这样您就可以将类型繁重的内容完全留给编译器。公平地说,在撰写本文时,系统仍然是w.i.p.第一批OpenCL编译程序正在测试中,但最重要的好处是快速原型设计,与优化的cuda相比,性能几乎相同。

答案 1 :(得分:1)

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应该内联,您可以使用开关或if用于不同的功能。由于F的输出未知,因此您必须使用更多内存。每个内核实例必须知道正确的内存写入和读取的位置,因此必须有一些最大大小(如果没有,那么这一切都没用,你必须使用CPU!)。如果不同的大小是稀疏的,那么我会在将数组返回到RAM并使用CPU计算这些数量之后使用计算这些不同大小的内容,同时使用一些零或指示值填充outA。

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

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

答案 2 :(得分:1)

你想要做的是使用高频调度将值快速发送到GPU,然后显示基本上是纹理查找和一些参数的函数的结果。

我想说如果满足两个条件,这个问题只值得在GPU上解决:

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

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

  3. 如果你可以肯定地回答这两个问题,那么只考虑使用GPU来解决你的问题,否则这两个因素将超过GPU为你提供的计算速度。

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

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

    我希望这是足够的信息,可以根据您是否应该使用GPU来解决问题。

    修改

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

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