将Java与Nvidia GPU一起使用(cuda)

时间:2014-04-04 15:27:18

标签: java cuda gpu-programming multi-gpu

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

我们订购了一些cuda gpu来尝试,因为cuda不支持Java,我想知道从哪里开始。我应该构建一个JNI接口吗?我应该使用JCUDA还是有其他方式?

我没有这方面的经验,我想如果有人能指导我,我就可以开始研究和学习了。

5 个答案:

答案 0 :(得分:385)

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

这可能听起来令人困惑,因为您基本上可以在GPU上计算任何。当然,关键是你是否能达到很好的加速。这里最重要的分类是问题是任务并行还是数据并行。粗略地说,第一个涉及多个线程正在处理自己的任务的问题,或多或少是独立的。第二个是指许多线程都做同样的问题 - 但是在数据的不同部分。

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

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

这导致另一种问题分类。即问题是内存绑定还是计算绑定

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

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

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

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

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

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


一般性评论:您特别要求CUDA。但我强烈建议您也看一下OpenCL。它有几个优点。首先,它是独立于供应商的开放式行业标准,AMD,Apple,Intel和NVIDIA都在实施OpenCL。此外,Java世界中对OpenCL有更广泛的支持。我想要使​​用CUDA的唯一情况是你想要使用CUDA运行时库,例如用于FFT的CUFFT或用于BLAS的CUBLAS(矩阵/向量操作)。虽然有一些方法可以为OpenCL提供类似的库,但是除非您为这些库创建自己的JNI绑定,否则它们不能直接在Java端使用。


您可能还会发现有趣的是,在2012年10月,OpenJDK HotSpot小组启动了该项目" Sumatra":http://openjdk.java.net/projects/sumatra/。该项目的目标是在JVM的支持下,在JVM中直接提供 GPU支持。可以在http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev

的邮件列表中查看当前状态和第一批结果

然而,不久前,我收集了一些与GPU上的Java相关的资源"一般来说。我将在这里再次总结这些内容,没有特别的顺序。

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

(字节)代码转换和OpenCL代码生成:

https://github.com/aparapi/aparapi:由AMD创建并积极维护的开源库。在一个特殊的"内核" class,可以覆盖应该并行执行的特定方法。使用自己的字节码读取器在运行时加载此方法的字节代码。代码被转换为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:OpenCL的Scala绑定。允许特殊的Scala集合与OpenCL并行处理。在集合元素上调用的函数可以是通常的Scala函数(有一些限制),然后将它们转换为OpenCL内核。

语言扩展

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

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绑定。似乎不再维护了


答案 1 :(得分:3)

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

答案 2 :(得分:1)

根据我所做的研究,如果您以Nvidia GPU为目标并且决定通过openCL使用Cuda,我发现了三种在Java中使用Cuda api的方法。

  1. JCuda(或替代方法)-http://www.jcuda.org/对于我正在解决的问题,这似乎是最好的解决方案。 JCuda中提供了许多库,例如CUBLAS。内核仍然是用C编写的。
  2. JNI-JNI接口不是我最喜欢编写的,但功能非常强大,可以让您做Cuda可以做的任何事情。
  3. JavaCPP-这基本上使您可以在Java中创建JNI接口,而无需直接编写C代码。https://stackoverflow.com/a/12871248/8692546这里有一个示例,说明如何通过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,它们可能能够执行所需的操作,而无需直接编写内核代码。不过,我并没有对它们进行过多研究。

答案 3 :(得分:1)

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

如果您正在寻找不使用Cuda / OpenCL内核而使用GPU的方法,我想添加一个对finmath-lib-cuda-extensions(finmath-lib-gpu-extensions){ 3}}(免责声明:我是该项目的维护者)。

确切地说,该项目提供了“向量类”的实现,即称为RandomVariable的接口,该接口提供了算术运算并减少了向量。有针对CPU和GPU的实现。有一些使用算法差异或简单估值的实现方式。

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

GPU实现使用JCuda和JOCL,并且可用于NVIDIA和ATI GPU。

该库是Apache 2.0,可以通过Maven Central使用。

答案 4 :(得分:0)

关于问题的性质和数据的信息很少,因此很难建议。但是,建议您评估其他解决方案的可行性,因为这些解决方案可以更轻松地与java集成,并可以进行水平和垂直缩放。我建议首先看的是一个名为Apache Spark https://spark.apache.org/的开源分析引擎,该引擎可在Microsoft Azure上使用,但也可能在其他云IaaS提供程序上使用。如果您坚持使用GPU,那么建议您看一下适合您组织预算的市场上其他GPU支持的分析数据库。