写点什么

用 TornadoVM 让 Java 性能更上一个台阶

作者:Juan Fumero

  • 2022-03-29
  • 本文字数:5506 字

    阅读完需:约 18 分钟

用TornadoVM让Java性能更上一个台阶

在 QCon Plus 大会上,Juan Fumero 谈到了 TornadoVM,一种Java虚拟机(JVM)高性能计算平台。Java 开发人员可以通过它在 GPU、FPGA 或多核 CPU 上自动运行程序。

 

像 GPU 这样的异构设备几乎出现在现今的所有计算系统中。例如,移动设备配备了一个多核 CPU 和一个集成 GPU;笔记本电脑通常有两个 GPU,一个与主 CPU 集成,另一个有专门用途(通常用于游戏)。甚至连数据中心也在集成像 FPGA 这样的设备。因此,异构设备将会继续存在。

 

所有这些设备都有助于提升性能和运行更有效的工作负载。当前和未来计算系统的程序员需要在各种各样的计算设备上处理程序执行。但是,很多并行编程框架都是基于 C 和 C++,使用高级编程语言(如 Java)开发的这类系统几乎是不存在的。这就是为什么我们要推出 TornadoVM。

 

简单地说,TornadoVM 是一个针对 Java 和 JVM 的高性能计算编程平台,可以在运行时将 Java 代码加载到异构硬件加速器上运行。

 

TornadoVM 提供了一个 Parallel Loop API 和一个 Parallel Kernel API。在这篇文章中,我们将分别介绍它们,并提供一些性能测试基准,还将分享 TornadoVM 如何将 Java 代码转译成可在并行硬件上执行的机器码。最后,我们将介绍 TornadoVM 在行业中的应用情况,包括一些应用场景。

 

GPU 和 FPGA 的快速通道

 

现如今,高级编程语言是如何访问异构硬件的?下图展示了一些硬件(CPU、GPU、FPGA)和高级编程语言(如 Java、R 语言或 Python)的例子。

 


看一下 Java,我们会发现它是在虚拟机中运行的。其中,OpenJDK、GraalVM 和 Corretto 都是虚拟机(VM)实现。本质上,Java 源代码被编译成 Java 字节码,然后 VM 执行这些字节码。如果应用程序运行得很频繁,虚拟机可以通过将频繁执行的方法编译成机器码的方式来进行优化——但这仅针对 CPU。

 

如果开发人员想要访问异构设备,比如 GPU 或 FPGA,他们通常需要通过 Java 本地接口(JNI)库来实现。

 

程序员必须导入一个库,并通过 JNI 调用这个库。程序员可以通过使用这些库为特定的 GPU 优化应用程序。但如果应用程序或 GPU 发生变化,可能需要重新构建应用程序,或需要重新调整优化参数。类似地,对于不同的 FPGA 甚至是其他型号的 GPU 也是如此。

 

因此,没有一个完整的 JIT 编译器和运行时能够像 CPU 那样处理异构设备,检测频繁执行的代码,并生成优化的机器码。而 TornadoVM 就是为此而生的。

 


TornadoVM 可以与现有的 JDK 结合使用。它是 JDK 的一个插件,程序员可以借助它在异构硬件上运行应用程序。目前,TornadoVM 可以运行在多核 CPU、GPU 和 FPGA 上。

 

硬件特征和并行化

 

下一个问题是,为什么要支持这么多硬件?目前正在考虑支持三种不同的硬件架构:CPU、GPU 和 FPGA。每种架构都针对不同类型的工作负载进行了优化。

 


优化 CPU 是为了降低应用程序的延迟,优化 GPU 是为了提高吞吐量。FPGA 介于二者之间:由于应用程序被物理连接到硬件上,FPGA 通常可以实现较低的延迟和较高的吞吐量。

 

我们将这些架构与现有的并行化类型映射起来。在上图中,我们可以发现并行化主要有三种类型:任务并行化、数据并行化和管道并行化。

 

通常,CPU 是为任务并行化而优化的,这意味着每个内核可以运行不同且独立的任务。相比之下,GPU 是为运行并行数据而优化的,这意味着执行的函数和内核是相同的,但输入数据不一样。最后,FPGA 非常适用于管道并行化,即不同指令的执行在不同的内部阶段之间会重叠。

 

理想情况下,我们需要一个高级并行编程框架,可以它表达不同类型的并行性,从而最大化每种设备类型的性能。现在,让我们看看 TornadoVM 是如何构建的,以及开发人员如何用它来表达不同类型的并行性。

 

TornadoVM 概览

 

TornadoVM 是 JDK(Java 开发工具包)的插件,Java 开发人员可以用它在异构硬件上自动执行程序。TornadoVM 的主要特性如下:

 

它有一个优化的 JIT(Just In Time)编译器,会针对每一种架构优化代码。这意味着为 GPU 生成的代码不同于为 CPU 和 FPGA 生成的代码,从而最大化每种架构的性能。

 

TornadoVM 可以实现架构之间、设备之间的动态任务迁移。例如,它可以先在 GPU 上运行应用程序一段时间,然后根据需要将其迁移到另一个 GPU、FPGA 或多核 CPU 上,无需重新启动应用程序。

 

TornadoVM 是完全硬件无关的:在异构硬件上运行的应用程序源代码与在 GPU、CPU 和 FPGA 上运行的是一样的。

 

最后,它可以与多种 JDK 结合适用。它是开源的(可以在GitHub上获得),Docker 镜像也可以在 NVIDIA 和 Intel 集成 GPU 上运行。

 

TornadoVM 系统栈

 

让我们来看一下 TornadoVM 的系统栈。在顶层,TornadoVM 暴露了一个 API,这是因为虽然它要利用并行化,但不检测。因此,它需要一种方法来识别应用程序源代码中哪些地方使用了并行化。

 


TornadoVM 提供了一个基于任务的编程 API,每个任务对应一个现有的 Java 方法。也就是说,TornadoVM 是在方法级编译代码,就像 JDK 或 JVM 那样,但编译的代码是面向 GPU 和 FPGA 的。我们也可以在方法中使用注解来指示并行化。另外,方法可以分成任务组,在同一个编译单元中进行编译。编译单元叫作 Task-Schedule: Task-Schedule 有一个名字(用于调试和优化),并包含了一组任务。

 

TornadoVM 引擎读入字节码级别的表达式,并自动为不同的架构生成代码。它目前有三个生成代码的后端,分别生成 OpenCL、CUDA 和 SPIR-V 代码。开发人员可以选择使用哪一个,或者让 TornadoVM 默认选择一个。

 

模糊滤镜示例

 

我们现在来看一个 TornadoVM 如何加速 Java 应用程序的例子:模糊滤镜。我们有一张图片,想要对这张图片应用模糊效果。

 

在了解如何编写代码之前,我们先来看看这个应用程序在异构硬件上运行的性能。下图显示了四种不同实现的测试基准。我们将 Java 的串行实现作为参考,y 轴是相对于参考的性能增益,越高表示性能越好。

 


左边的两列表示基于 CPU 的执行结果。第一列使用标准的 Java 并行流,第二列使用运行在多 CPU 核心上的 TornadoVM,分别获得 11 倍和 17 倍的加速。TornadoVM 获得更好的结果,因为它为 CPU 生成了 OpenCL 代码,而 OpenCL 非常擅长使用向量单位对代码进行矢量化。如果应用程序在集成显卡上运行,与 Java 串行实现相比,可以获得 19 倍的性能加速。如果在 NVIDIA GPU(2060)上运行应用程序,可以获得高达 340x 的性能加速(使用 TornadoVM 的 OpenCL 后端)。我们将性能加速与 Java 并行流相比,当在 NVIDIA GPU 上运行时,TornadoVM 可以获得 30 倍的性能加速。

 

模糊滤镜的实现

 

模糊滤镜是一种映射操作符,将一个函数(模糊效果)应用在每一个输入的图像像素上。这种模式非常适合进行并行化,因为每个像素都可以独立于其他像素进行计算。

 

我们要做的第一件事是在 Java 方法中给代码添加注解,让 TornadoVM 知道如何并行化它们。

 


因为每一个像素的计算可以并行进行,所以我们将 @Parallel 注解添加到最外层的两个循环中。这将向 TornadoVM 发出信号,让它完全并行计算这两个循环。代码注解定义了数据并行化模式。

 

第二件事情是定义任务。由于输入的是 RGB 图像,我们可以为每个颜色通道(红、绿、蓝)创建一个任务。因此,我们要做的是对每个通道进行模糊处理。我们使用了一个包含三个任务的 TaskSchedule 对象。

 


此外,我们还需要定义哪些数据将从 Java 内存堆传输到设备(例如 GPU)上。这是因为 GPU 和 FPGA 通常不共享内存。因此,我们需要一种方法来告诉 TornadoVM 需要在设备之间复制哪些内存区域。这是通过 streamIn()和 streamOut()函数来完成的。

 

然后是定义任务集,每个颜色通道一个任务。它们有名字标识,并通过方法引用组合在一起。这个方法现在可以被编译成内核代码。

 

最后,我们调用 execute 函数,在设备上并行执行这些任务。现在我们来看看 TornadoVM 是如何编译和执行代码的。

 

TornadoVM 如何在并行硬件上启动 Java 内核

 

原始的 Java 代码是单线程的,即使已经加了 @Parallel 注解。在 execute()函数被调用时,TornadoVM 开始优化代码。

 

首先,代码被编译成一种中间表示,以便对其进行优化(TornadoVM 扩展了 Graal JIT 编译器,所有的优化都发生在这一层)。然后,TornadoVM 将优化后的代码转换成高效的 PTX、OpenCL 或 SPIR-V 代码。

 

这个时候开始执行代码,将会启动数百或数千个线程。TornadoVM 会启动多少个线程取决于应用程序。

 


在这个例子中,模糊滤镜有两个并行循环,每个循环遍历一个图像维度。因此,在运行时编译期间,TornadoVM 创建了一个与输入图像具有相同维度的线程网格。每个网格单元(也就是每个像素)映射一个线程。例如,如果图像的像素是 2000x2000,TornadoVM 将在目标设备(例如 GPU)上启动 2000x2000 个线程。

 

TornadoVM 还可以实现管道并行化,主要是针对 FPGA。当我们或 TornadoVM 选择了 FPGA,它会自动将生成代码的信息插入到管道指令中。与之前的并行代码相比,这种策略可以将性能提高一倍

 

Parallel Loop API 与 Parallel Kernel API

 

现在我们来看看如何在 TornadoVM 中表示计算内核。TornadoVM 有两个 API:一个是我们在模糊滤镜示例中使用的 Parallel Loop API,另一个是 Parallel Kernel API。TornadoVM 的并行循环 API 是基于注解的。在使用这个 API 时,开发人员必须提供串行实现代码,然后考虑在哪里并行化循环。

 

一方面,开发速度加快了,因为开发人员只需要向现有的 Java 串行代码中添加注解就可以实现并行化。Parallel Loop API 适合非专业用户,他们不需要知道 GPU 的计算细节,也不需要知道应该使用哪种硬件。

 

另一方面,Parallel Loop API 可以使用的模式数量有限。在使用这个 API 时,开发人员可以使用典型的map/reduce模式运行应用程序。但其他的并行模式,如扫描或复杂模板,很难用这个 API 实现。此外,这个 API 不允许开发人员控制硬件,因为它是硬件无关的,但有时候开发人员确实需要控制硬件。此外,将现有的 OpenCL 和 CUDA 代码移植到 Java 可能会很困难。

 

为了应对这些限制,我们加入了 Parallel Kernel API。

 

用 Parallel Kernel API 实现模糊滤镜

 


我们回到之前的例子:模糊滤镜。我们有两个并行循环,遍历图像的两个维度并应用滤镜。这可以转换成使用 Parallel Kernel API。

 

我们不使用两个循环,而是通过内核上下文引入隐式并行化。上下文是一个 TornadoVM 对象,用户可以通过它访问到每个维度的线程标识符、本地/共享内存、同步原语等。

 

在我们的示例中,滤镜的 X 轴和 y 轴坐标分别来自上下文的 globalIdx 和 globalIdy 属性,并像之前一样用于应用滤镜。这种编程风格更接近 CUDA 和 OpenCL 编程模型。

 


需要注意的是,TornadoVM 无法在运行时确定需要多少个线程。用户需要通过 worker 网格进行配置。

 

在这个例子中,我们用图像维度创建了一个 2D 的 worker 网格,并与函数名相关联。当用户的代码调用 execute()函数时,将网格作为参数传进去,进而应用相应的滤镜。

 

TornadoVM 的优势

 

但是,如果 Parallel Kernel API 更接近于底层的编程模型,为什么要使用 Java 而不是 OpenCL 和 PTX 或 CUDA 和 PTX,尤其是在有现有代码的情况下?

 

TornadoVM 还有其他的优势,比如实时任务迁移、自动内存管理和透明的代码优化,而且代码优化是根据不同的架构而进行的。

 

它还可以运行在 FPGA 上,具有完全透明和集成的编程工作流。你可以使用你最喜欢的 IDE,例如 IntelliJ 或 Eclipse,编写在 FPGA 上运行的代码。

 

它也可以部署在云端,如亚马逊云。你可以将代码移植到 Java 和 TornadoVM,以便免费获得所有这些功能。

 

性能

 

现在我们来谈谈性能。TornadoVM 可不仅仅被用于给图像应用滤镜,它也被用在金融科技或数学模拟(例如 Monte Carlo 或 Black-Scholes)领域。它还被用于计算机视觉应用、物理模拟、信号处理等其他领域。

 


上图对不同设备上的不同应用程序的执行情况进行了对比。同样,我们仍然将串行执行作为参考对象,条形表示加速因子,越高越好。

 

正如我们所看到的,我们有可能实现非常高的性能加速。例如,信号处理或物理模拟可以比Java的串行执行快4000倍。对这些结果的详细分析,可以参阅学术出版物

 

TornadoVM 在行业的应用

 


业界的一些公司也在尝试使用 TornadoVM。上图是两个正在使用 TornadoVM 的应用场景。

 

其中一个应用场景来自卢森堡的 Neurocom 公司,用它运行一种自然语言处理算法。到目前为止,通过在 GPU 上运行分层聚类算法,已经实现了 30 倍的性能提升。

 

另一个应用场景来自 Spark Works 公司,这是一家位于爱尔兰的公司,用它处理来自物联网设备的信息。他们用强大的 GPU、GPU100 运行后处理工作负载。与 Java 相比,他们可以获得 460 倍的性能提升,已经相当好了。

 

你可以访问TornadoVM网站查看完整的应用场景列表。

 

总结

 

异构设备现在几乎出现在每一个计算系统中,这是不可避免的。它们就在这里,而且会一直这样下去。

 

因此,当前和未来软件系统的程序员需要面对广泛和多样化的设备,例如 GPU、FPGA 或任何其他即将出现的硬件。他们可以通过 TornadoVM 对这些设备进行编程。

 

TornadoVM 可以被看作是 Java 和 JVM 的高性能计算平台,可以与现有的 JDK(例如 OpenJDK)结合使用。

 

本文介绍了 TornadoVM,并简要解释了它的工作原理。此外,本文还通过一个用 Java 实现的图像处理示例演示了开发人员如何充分利用异构硬件。我们解释了 TornadoVM 中用于异构编程的两个 API:一个是 Parallel Loop API,适合普通开发人员使用;另一个是 Parallel Kernel API,适合已经了解 CUDA 和 OpenCL 并想要将现有代码移植到 TornadoVM 的专家使用。

 

作者简介:


 Juan Fumero 是曼彻斯特大学的一名研究员。他的研究课题是异构高级编程语言虚拟机、GPGPU 和分布式计算。目前,他是 TornadoVM 项目的一员,为 Java 应用程序引入自动 GPU 和 FPGA JIT 编译和执行特性。他还与英特尔合作,将 oneAPI 引入 TornadoVM,用于对英特尔计算架构的代码进行优化。Juan 获得了爱丁堡大学的博士学位,主要研究在 GPU 上加速 Java、R 语言和 Ruby。此外,他曾在 Oracle 实验室和 CERN 实习,实现编译器,评估多核系统的并行技术。

 

原文链接

Level up Your Java Performance with TornadoVM

2022-03-29 12:385280

评论 1 条评论

发布
用户头像
这个利器要是捐献给OpenJDK社区,进入mainline就牛逼了!
2022-04-02 11:12
回复
没有更多了
发现更多内容

分布式事务及其一致性协议

爱好编程进阶

Java 程序员 后端开发

[数据分析]-音频分析-BirdCLE-1

浩波的笔记

人工智能 AI 数据分析

报告解读下载 | 5月《中国数据库行业分析报告》重磅发布!精彩抢先看!

墨天轮

数据库 oracle opengauss TiDB 国产数据库

阿里Java面试必问:JVM与性能优化+Redis+设计模式+分布式

Java浪潮

编程 架构

运维审计堡垒机哪款好?报价贵吗?

行云管家

网络安全 堡垒机 IT运维 运维审计

毕业总结

凌波微步

架构训练营

洞见科技纪凯:基于隐私计算的「客户增长」生态

洞见科技

金融科技 隐私计算

堡垒机是服务器吗?两者有区别吗?

行云管家

运维 网络安全 服务器 堡垒机

CRM系统的18个关键功能

低代码小观

CRM 客户关系管理 企业管理系统 CRM系统 客户关系管理系统

借鉴这份Java进阶架构师之路的核心知识,我成功收获蚂蚁金服、阿里、美团等Offer

Java浪潮

Java 架构 java架构

SpringCloud 中 Gateway 是如何关联对应的 GatewayFilterFactory 和 参数解析的

gin

Gateway SpringCould

进阶中的程序员:深入解析 Spring boot(含PDF文档分享)

Java浪潮

spring 编程 JAVA开发 springboot

华为手机用户找歌不用愁,HarmonyOS 2版本更新上线“听歌识曲”

科技汇

参与 Apache 顶级开源项目的 N 种方式,Apache Dubbo Samples SIG 成立!

爱好编程进阶

Java 程序员 后端开发

这么好用的低延时直播,网易云信竟然将它开源了?!

网易云信

开源 WebRTC 音视频技术

Hacker 资讯 | 5 月上旬区块链黑客松活动汇总

One Block Community

区块链

原来,嵌入式BI方案的核心差异全在这,教你如何评估!

葡萄城技术团队

数据分析 BI 嵌入式软件 核心差异

黄吉:如何适配OpenHarmony自有音频框架ADM?

OpenHarmony开发者

OpenHarmony

网络协议之:memcached binary protocol详解

程序那些事

Java 网络协议 程序那些事 5月月更

手把手推导Back Propagation

OneFlow

人工智能 神经网络 深度学习 Back Propagation 推导过程

作为Java程序员连Redis都不会?阿里架构师带你深入“解剖”Redis

Java浪潮

redis JAVA开发 java程序员

字节面试到底有多难,一个Hadoop源码就拦住了百分之90的人群

爱好编程进阶

Java 程序员 后端开发

微服务网关除了zuul、spring cloud gateway还有更出色的

爱好编程进阶

Java 程序员 后端开发

阿联酋航空与华为进一步深化合作伙伴关系

最新动态

如何制作优秀的产品说明手册?

小炮

产品说明手册

深入浅出Redis丨阿里架构师的Redis实战心得,让你毫不费力的学习

Java浪潮

redis java程序员 java架构

AI简报:Blind超分KernelGAN

AIWeker

人工智能 深度学习 机器视觉 5月月更 超分

导师男团来袭 | 开源之夏2022,与Alluxio一起探索数据编排的奇妙世界

Alluxio

开源 大学生 #开源项目 开源之夏 数据编排

云原生基础治理平台SchedulX V1.2.0正式发布,助力企业降本增效

星汉未来

运维 云原生 星汉未来

GAIA数据集V1.10更新

云智慧AIOps社区

运维 AIOPS 数据集

【OpenCV】配置OpenCV教程,OpenCV入门

恒山其若陋兮

5月月更

用TornadoVM让Java性能更上一个台阶_语言 & 开发_InfoQ精选文章