将Java与Nvidia GPU(CUDA)结合使用



我正在做一个用Java完成的商业项目,它需要巨大的计算能力来计算商业市场。简单的数学,但有大量的数据。

我们订购了一些CUDA GPU来试用,由于CUDA不支持Java,我想知道从哪里开始。我应该构建一个JNI接口吗?我应该使用JCUDA还是有其他方法?

我没有这方面的经验,我希望有人能指导我做一些事情,这样我就可以开始研究和学习了。

首先,您应该意识到CUDA不会自动加快计算速度。一方面,因为GPU编程是一门艺术,要把它做好可能非常非常具有挑战性。另一方面,因为GPU只适用于某些类型的计算。

这听起来可能令人困惑,因为你基本上可以在GPU上计算任何。当然,关键是你是否能很好地加速。这里最重要的分类是问题是任务并行还是数据并行多线程都在做相同的,但在数据的不同部分。

后者是GPU擅长的问题:它们有多个内核,所有内核都做同样的事情,但对输入数据的不同部分进行操作。

你提到你有"简单的数学,但有大量的数据"。尽管这听起来像是一个完美的数据并行问题,因此非常适合GPU,但还有另一个方面需要考虑:GPU在理论计算能力方面快得离谱(FLOPS,每秒浮点运算)。但它们经常被内存带宽所限制。

这就引出了另一类问题。即问题是内存限制还是

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

第二个术语"计算绑定"指的是指令数量与内存读/写数量相比较高的问题。例如,考虑矩阵乘法:当n是矩阵的大小时,指令数将为O(n^3)。在这种情况下,可以预期GPU在一定矩阵大小下将优于CPU。另一个例子可能是在"少数"数据元素上执行许多复杂的三角计算(正弦/余弦等)。

根据经验:您可以假设从"主"GPU内存读取/写入一个数据元素的延迟约为500条指令。。。。

因此,GPU性能的另一个关键点是数据位置:如果您必须读取或写入数据(在大多数情况下,您必须;-),那么您应该确保数据尽可能靠近GPU核心。因此,GPU具有特定的内存区域(称为"本地内存"或"共享内存"),其大小通常只有几KB,但对于即将参与计算的数据来说尤其有效。

因此,再次强调这一点:GPU编程是一门艺术,它与CPU上的并行编程仅相距甚远。Java中的Threads之类的东西,以及ThreadPoolExecutorsForkJoinPools等所有的并发基础设施,可能会给人留下这样的印象,即您只需要以某种方式拆分工作,并将其分布在几个处理器之间。在GPU上,您可能会遇到更低级别的挑战:占用率、寄存器压力、共享内存压力、内存合并。。。仅举几个例子。

然而,当你有一个数据并行、计算受限的问题需要解决时,GPU是最好的选择。


一般备注:您特别要求CUDA。但我强烈建议你也去看看OpenCL。它有几个优点。首先,它是一个独立于供应商的开放行业标准,AMD、苹果、英特尔和NVIDIA都实现了OpenCL。此外,在Java世界中对OpenCL有更广泛的支持。我更愿意接受CUDA的唯一情况是,当你想使用CUDA运行库时,比如用于FFT的CUFFT或用于BLAS(矩阵/矢量运算)的CUBLAS。尽管有一些方法可以为OpenCL提供类似的库,但它们不能直接从Java端使用,除非您为这些库创建自己的JNI绑定。


您可能也会发现有趣的是,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/)

(字节)代码翻译和OpenCL代码生成:

https://github.com/aparapi/aparapi:由AMD创建并积极维护的开源库。在一个特殊的"内核"类中,可以覆盖一个应该并行执行的特定方法。此方法的字节码在运行时使用自己的字节码读取器加载。该代码被翻译成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代码的库,然后将CUDA代码编译为在GPU上执行代码的库。该图书馆是在一篇博士论文的背景下开发的,其中包含了关于翻译过程的深刻背景信息。

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

语言扩展

http://www.ateji.com/px/index.html:Java的一种语言扩展,允许并行构造(例如循环的并行、OpenMP风格),然后在GPU上使用OpenCL执行。不幸的是,这个非常有希望的项目已不再维持。

http://www.habanero.rice.edu/Publications.html(JCUDA):一个库,可以将特殊的Java代码(称为JCUDA代码)转换为Java和CUDA-C代码,然后可以在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绑定:是原始OpenCL 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绑定。似乎不再维护


根据我所做的研究,如果您的目标是Nvidia GPU,并决定在OpenCL上使用CUDA,我找到了在java中使用CUDA API的三种方法。

  1. JCuda(或备选方案)-http://www.jcuda.org/.这似乎是解决我正在处理的问题的最佳方案。JCuda提供了许多库,如CUBLAS。内核仍然是用C写的
  2. JNI-JNI接口不是我最喜欢写的,但它非常强大,可以让你做CUDA能做的任何事情
  3. JavaCPP-这基本上允许您在不直接编写C代码的情况下用Java创建JNI接口。这里有一个例子:在Java中运行工作CUDA代码的最简单方法是什么如何与CUDA推力一起使用。对我来说,这似乎就像你可以写一个JNI接口一样

所有这些答案基本上都只是在Java中使用C/C++代码的方法。你应该问问自己为什么需要使用Java,如果你不能用C/C++来代替。

如果你喜欢Java,知道如何使用它,不想处理所有的指针管理以及C/C++所没有的东西,那么JCuda可能就是答案。另一方面,CUDA Thrust库和其他类似的库可以用于在C/C++中进行大量指针管理,也许您应该看看这一点。

如果您喜欢C/C++并且不介意指针管理,但还有其他限制因素迫使您使用Java,那么JNI可能是最好的方法。不过,如果您的JNI方法只是内核命令的包装器,那么您还可以使用JCuda。

JCuda有一些替代品,如Cuda4J和Root Beer,但这些似乎没有得到保留。鉴于在撰写本文时,JCuda支持CUDA 10.1。这是最新的CUDA SDK。

此外,还有一些使用CUDA的java库,如deeplearning4j和Hadoop,它们可以在不需要直接编写内核代码的情况下完成您想要的任务。不过,我并没有过多地研究它们。

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

Marco13已经提供了一个极好的答案。

如果你正在寻找一种在不实现CUDA/OpenCL内核的情况下使用GPU的方法,我想添加一个对finmath-lib-CUDA扩展的引用(finmath-lib GPU扩展)http://finmath.net/finmath-lib-cuda-extensions/(免责声明:我是这个项目的维护者)。

该项目提供了一个"向量类"的实现,确切地说,是一个名为RandomVariable的接口,它提供了对向量的算术运算和归约。有CPU和GPU的实现。有使用算法差异化或简单估值的实现。

GPU上的性能改进目前很小(但对于大小为100.000的矢量,您可能会得到大于10的性能改进)。这是由于内核尺寸较小。这将在未来的版本中得到改进。

GPU实现使用JCuda和JOCL,可用于Nvidia和ATI GPU。

该库是Apache 2.0,可通过Maven Central获得。

关于问题的性质和数据的信息不多,因此很难提出建议。然而,我们建议评估其他解决方案的可行性,这些解决方案可以更容易地与java集成,并支持水平和垂直扩展。我建议首先看一个名为ApacheSpark的开源分析引擎https://spark.apache.org/这在Microsoft Azure上可用,但可能也在其他云IaaS提供商上可用。如果你坚持让你的GPU参与进来,那么建议你看看市场上其他GPU支持的分析数据库,这些数据库符合你组织的预算。

相关内容

  • 没有找到相关文章

最新更新