159

I'm working on a business project that is done in Java, and it needs huge computation power to compute business markets. Simple math, but with huge amount of data.

We ordered some CUDA GPUs to try it with and since Java is not supported by CUDA, I'm wondering where to start. Should I build a JNI interface? Should I use JCUDA or are there other ways?

I don’t have experience in this field and I would like if someone could direct me to something so I can start researching and learning.

4

5 回答 5

484

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

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

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

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

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

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

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

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

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

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

但是,当您要解决数据并行、计算受限的问题时,GPU 是您的最佳选择。


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


您可能还会觉得有趣的是,2012 年 10 月,OpenJDK HotSpot 小组启动了“Sumatra”项目:http: //openjdk.java.net/projects/sumatra/。该项目的目标是在 JIT 的支持下直接在 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 创建并积极维护的开源库。在一个特殊的“内核”类中,可以覆盖应该并行执行的特定方法。此方法的字节码在运行时使用自己的字节码阅读器加载。代码被翻译成 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 绑定。允许与 OpenCL 并行处理特殊的 Scala 集合。在集合元素上调用的函数可以是通常的 Scala 函数(有一些限制),然后将其转换为 OpenCL 内核。

语言扩展

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

http://www.habanero.rice.edu/Publications.html (JCUDA):一个可以将特殊的 Java 代码(称为 JCUDA 代码)翻译成 Java 和 CUDA-C 代码的库,然后可以在显卡。但是,该库似乎并不公开。

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


于 2014-04-04T17:10:02.600 回答
4

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

  1. JCuda(或替代)- http://www.jcuda.org/。这似乎是我正在处理的问题的最佳解决方案。JCuda 中提供了许多库,例如 CUBLAS。内核仍然是用 C 编写的。
  2. JNI - JNI 接口不是我最喜欢写的,但是非常强大,可以让你做任何 CUDA 可以做的事情。
  3. JavaCPP - 这基本上可以让您在 Java 中创建 JNI 接口,而无需直接编写 C 代码。这里有一个例子:在 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,它们可能能够完成您正在寻找的工作,而无需您直接编写内核代码。不过,我并没有过多地研究它们。

于 2019-08-02T22:16:33.413 回答
3

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

于 2014-04-04T16:34:02.750 回答
2

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

如果您正在寻找一种在不实现 CUDA/OpenCL 内核的情况下使用 GPU 的方法,我想添加对 finmath-lib-cuda-extensions (finmath-lib-gpu-extensions) 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 获得。

于 2019-09-12T13:57:16.223 回答
1

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

于 2020-10-17T22:46:35.530 回答