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

Posted

技术标签:

【中文标题】将 Java 与 Nvidia GPU (CUDA) 结合使用【英文标题】:Using Java with Nvidia GPUs (CUDA) 【发布时间】:2014-05-17 00:13:23 【问题描述】:

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

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

我在这个领域没有经验,我希望有人可以指导我做某事,以便我可以开始研究和学习。

【问题讨论】:

GPU 将帮助您加速特定类型的计算密集型问题。但是,如果您有大量数据,则更有可能受到 IO 限制。 GPU 很可能不是解决方案。 "使用 GPGPU 提升 Java 性能" --> arxiv.org/abs/1508.06791 有点悬而未决的问题,我很高兴模组没有关闭它,因为 Marco13 的回答非常有帮助!应该是维基恕我直言 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

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

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

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

这导致了问题的另一种分类。即问题是 memory bound 还是 compute bound

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

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

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

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

所以再次强调这一点:GPU 编程是一门艺术,它只与 CPU 上的并行编程远程相关。诸如 Java 中的线程之类的东西,以及 ThreadPoolExecutorsForkJoinPools 等所有并发基础设施可能会给人一种印象,即您只需以某种方式拆分工作并将其分配给多个处理器。在 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 代码的库,然后可以在 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 绑定。好像不再维护了


【讨论】:

考虑将两个矩阵相加并将结果存储在第三个矩阵中的操作。在没有 OpenCL 的 CPU 上进行多线程处理时,瓶颈始终是发生加法的步骤。这个操作显然是数据并行的。但是可以说我们事先不知道它是否会受到计算限制或内存限制。需要花费大量的时间和资源来实现,然后才能看到 CPU 在执行此操作时要好得多。那么如何在不实现 OpenCL 代码的情况下预先识别这一点。 @Cool_Coder 事实上,很难事先判断某个任务是否(或多少)将从 GPU 实现中受益。对于第一个直觉,可能需要一些不同用例的经验(我承认我也没有)。第一步可能是查看nvidia.com/object/cuda_showcase_html.html 并查看是否列出了“类似”问题。 (它是 CUDA,但它在概念上非常接近 OpenCL,以至于在大多数情况下可以传输结果)。在大多数情况下,还提到了加速,其中许多都有论文甚至代码的链接 +1 for aparapi - 这是一种在 java 中开始使用 opencl 的简单方法,并允许您轻松比较简单情况下的 CPU 与 GPU 性能。此外,它由 AMD 维护,但适用于 Nvidia 卡。 这是我在 *** 上见过的最好的回复之一。感谢您的时间和精力! @AlexPunnen 这可能超出了 cmets 的范围。据我所知,OpenCV 有一些 CUDA 支持,从 docs.opencv.org/2.4/modules/gpu/doc/introduction.html 开始。 developer.nvidia.com/npp 有很多图像处理例程,可能很方便。 github.com/GPUOpen-ProfessionalCompute-Tools/HIP 可能是 CUDA 的“替代方案”。 可能可以将此作为一个新问题提出,但必须小心正确措辞,以避免对“基于意见”/“请求第三方库”的反对意见...... 【参考方案3】:

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

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

【讨论】:

【参考方案4】:

Marco13 already provided an excellent answer.

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

该项目提供了“向量类”的实现,准确地说,是一个名为RandomVariable 的接口,它提供了向量的算术运算和归约。有 CPU 和 GPU 的实现。有使用算法微分或简单估值的实现。

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

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于将 Java 与 Nvidia GPU (CUDA) 结合使用的主要内容,如果未能解决你的问题,请参考以下文章

nvidia cuda访问gpu共享内存

tensorflow-gpu与cuda 关系

cuda11.7显卡要求

CUDA版本与GPU驱动版本问题

如何使用软件实现在没有 GPU 的情况下运行 CUDA?

什么是CUDA和CUDNN?——GeForce NVIDIA显卡用于深度学习计算的GPU加速工具