使用Java与Nvidia GPU(cuda)

我正在做一个用java完成的业务项目,需要计算机业务市场的巨大计算能力。 简单的math,但数据量巨大。

我们订购了一些cuda GPU来试用它,因为Java不支持cuda,我想知道从哪里开始。 我应该build立一个JNI接口? 我应该使用JQUDA还是有其他方法可以使用?

我没有在这个领域的经验,我想如果有人指示我的东西,那么我就可以开始研究和学习。

首先,您应该意识到CUDA不会自动快速地进行计算。 一方面,因为GPU编程是一门艺术,所以要做到这一点非常具有挑战性。 另一方面,因为GPU只适合某些types的计算。

这可能听起来很混乱,因为基本上可以在GPU上计算任何东西 。 当然,关键是你是否会达到很好的加速。 这里最重要的分类是问题是并行的还是并行的 。 第一个概念是指几个线程在自己的任务中工作的问题,或多或less是独立的。 第二个是指许多线程都在做相同的问题 – 但在数据的不同部分。

后者是GPU擅长的问题:它们有很多内核,而且所有内核都是一样的,只是在input数据的不同部分进行操作。

你提到你有“简单的math,但有大量的数据”。 虽然这可能听起来像是一个完美的数据并行问题,因此它非常适合GPU,但还有另一个方面需要考虑:在理论计算能力(FLOPS,每秒浮点运算)方面,GPU的速度非常快。 但是它们往往受内存带宽的限制。

这导致了另一个问题的分类。 即问题是内存限制还是计算限制

第一个是指每个数据元素的指令数量很less的问题。 例如,考虑一个平行向量加法:你必须读取两个数据元素,然后执行一个加法,然后将和写入结果向量。 在GPU上执行此操作时,不会看到加速,因为单个添加不能补偿读/写内存的工作量。

第二个术语“计算边界”指的是与内存读取/写入次数相比,指令数量较多的问题。 例如,考虑matrix乘法:当n是matrix的大小时,指令的数量是O(n ^ 3)。 在这种情况下,人们可以预期,GPU将以一定的matrix大小胜过CPU。 另一个例子可能是在很less的数据元素上执行许多复杂的三angular函数(正弦/余弦等)。

作为一个经验法则:您可以假设从“主”GPU内存中读取/写入一个数据元素的延迟约为500条指令….

因此,GPU性能的另一个关键点是数据局部性 :如果您必须读取或写入数据(并且在大多数情况下,您将不得不;)),那么您应该确保数据保持接近可能的GPU核心。 GPU因此具有一定的存储区域(称为“本地存储器”或“共享存储器”),其通常只有几KB的大小,但是对于即将涉及计算的数据来说特别有效。

所以再次强调这一点:GPU编程是一门艺术,它只与CPU上的并行编程有关。 像Java中的ThreadPoolExecutors ,像ThreadPoolExecutorsForkJoinPools等所有并发基础结构可能会给人一种印象,就是你必须以某种方式拆分你的工作,并将其分配到几个处理器中。 在GPU上,您可能会遇到一个更低级别的挑战:占用率,注册压力,共享内存压力,内存聚合……仅举几例。

但是,如果要解决数据并行,计算限制问题,那么GPU就是要走的路。


一般的评论:你特别要求CUDA。 但我强烈build议你也看看OpenCL。 它有几个优点。 首先,这是一个独立于供应商的开放式行业标准,还有由AMD,苹果,英特尔和NVIDIA公司实施的OpenCL。 此外,在Java世界中还有更广泛的OpenCL支持。 唯一一个我宁愿selectCUDA的情况就是当你想使用CUDA运行时库,比如用于FFT的CUFFT或用于BLAS(matrix/vector操作)的CUBLAS。 尽pipe有一些为OpenCL提供类似库的方法,但是除非您为这些库创build自己的JNI绑定,否则不能直接从Java方面使用它们。


你也许会发现有趣的是,在2012年10月,OpenJDK HotSpot小组启动了项目“Sumatra”: http : //openjdk.java.net/projects/sumatra/ 。 这个项目的目标是直接在JVM中提供GPU支持,在JIT的支持下。 目前的状态和第一个结果可以在他们的邮件列表中看到http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev


不过前一阵子,我收集了一些与“GPU上的Java”有关的资源。 我会在这里再次总结一下,没有特别的顺序。

免责声明 :我是http://jcuda.org/和http://jocl.org/的作者);

(Byte)代码翻译和OpenCL代码生成:

https://github.com/aparapi/aparapi :由AMD创build并主动维护的开源库。 在一个特殊的“Kernel”类中,可以重写一个应该并行执行的特定方法。 该方法的字节码在运行时使用自己的字节码阅读器加载。 代码被转换成OpenCL代码,然后使用OpenCL编译器进行编译。 然后可以在OpenCL设备上执行结果,该设备可以是GPU或CPU。 如果编译进OpenCL是不可能的(或者没有OpenCL可用),代码将仍然使用线程池并行执行。

https://github.com/pcpratts/rootbeer1 :一个用于将Java部分转换为CUDA程序的开源库。 它提供了专用的接口,可以实现这些接口来指示应该在GPU上执行某个类。 与Aparapi相比,它试图自动将“相关”数据(即对象图的完整相关部分)序列化为适合于GPU的表示。

https://code.google.com/archive/p/java-gpu/ :用于将带注释的Java代码(带有一些限制)转换为CUDA代码的库,然后将其编译为执行GPU上的代码的库。 该图书馆是在博士论文的背景下开发的,其中包含有关翻译过程的深刻背景资料。

https://github.com/ochafik/ScalaCL:Scala绑定OpenCL。; 允许特殊的Scala集合与OpenCL并行处理。 在集合元素上调用的函数可以是普通的Scala函数(有一些限制),然后将其转换成OpenCL内核。

语言扩展

http://www.ateji.com/px/index.html:Java的语言扩展,允许并行构造(例如并行循环,OpenMP样式),然后在OpenCL上在GPU上执行。; 不幸的是,这个非常有前途的项目已经不在了。

http://www.habanero.rice.edu/Publications.html(JCUDA ):一个可以将特殊Java代码(称为JCUDA代码)转换为Java代码和CUDA代码的库,然后可以编译并执行GPU。 但是,图书馆似乎并没有公开。

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html :用于OpenMP构造的Java语言扩展,带有CUDA后端

Java OpenCL / CUDA绑定库

https://github.com/ochafik/JavaCL :面向OpenCL的Java绑定:面向对象的OpenCL库,基于自动生成的低级绑定

http://jogamp.org/jocl/www/:OpenCL的; Java绑定:一个面向对象的OpenCL库,基于自动生成的低级绑定

http://www.lwjgl.org/:OpenCL的; Java绑定:自动生成的低级绑定和面向对象的便利类

http://jocl.org/:OpenCL的; Java绑定:原始Op​​enCL API的1:1映射的低级绑定

http://jcuda.org/:CUDA的; Java绑定:原始CUDA API的1:1映射的低级绑定

http://sourceforge.net/projects/jopencl/:OpenCL的; Java绑定。 似乎自2010年以来不再维持

http://www.hoopoe-cloud.com/:CUDA的; Java绑定。 似乎不再维护


我将首先使用Java和CUDA的其中一个项目: http : //www.jcuda.org/