即将开始的GIAC课程
Alibaba FFI
JAVA进阶
2021-07-30 16:50--17:50
案例背景:
Java广泛应用于开发现代的大型软件系统,例如数据库,图计算引擎,大数据计算平台。虽然Java有着语法简单,性能卓越,内存安全等特性,但是其依赖垃圾回收自动管理内存,无法提供编程可控内存管理,在实际生产使用中往往遇到由于垃圾回到引起的难以解决的性能瓶颈。因此,一些Java实现的数据平台往往自身带有off-heap的支持,通过序列化等类似减少创建Java对象的数量。这种方式的坏处是丧失了面向对象开发范式的带来便捷,且开发offheap的支持易错,难以复用,以及序列化和反序列化带来的开销。
此外,一些数据平台往往采用本地语言,例如C、C++或者Rust,进行开发。虽然这些本地语言开发的平台有着高性能的特点,但是却难以推广。这是由于平台的使用者,也就是算法开发者,往往倾向于使用更为简单灵活的抽象语言,例如Java和Python。因此,这些本地语言开发的平台往往会提供Java的SDK供开发者使用,但不幸的是,这些SDK和宿主平台之间的数据交互往往是一个挑战。通过JNI来访问宿主平台的数据有着不可接收的额外负载,实际中这些平台往往采用一些跨平台内存数据格式(例如Apache Arrow和FlatBuffers)。然而,这些内存数据格式往往有一些限制,例如不可以增加或者插入数据。
解决思路:
我们认为(1)数据在内存中使用最灵活和性能最优异的格式或许是是以本地语言创建和访问的对象,而(2)本地语言或许最适合Java offheap方案的开发。
以C++为例,我们可以采用C++进行面向对象设计和开发,之后通过一些FFI框架复用C++数据结构来开发Java的SDK。这样,我们可以减少复杂的offheap方案开发,享受C++自由便利的内存管理。这个想法的困难在于两者:(1)开发FFI困难,直接使用JNI的开发枯燥且易错,采用既有的抽象FFI框架要么不支持C++,要么支持的不好(例如不支持C++模板到Java泛型的映射)。(2)Java的FFI框架只能直接或者间接的通过JNI来实现,而JNI的额外负载在频繁的数据访问非常显著,难以接受。
为了解决FFI开发的困难,我们基于Java的注解机制,开发了一套DSL,用于将C++的数据结构映射到Java世界。我们通过基于注解处理器的自动代码生成技术,来自动的将C++的数据结构映射到Java,用户只需要用Java语言,开发一些Java接口来实现映射即可。
为了解决FFI性能的问题,我们采用Clang+LLVM技术编译C++和JNI代码。之后,我们收集编译过程中得到的LLVM bitcode,将Java native方法对应的bitcode转换成Java bytecode,并替换对应的native方法为Java字节码方法。这些Java bytecode可以保留C++代码的计算逻辑,计算出需要访问的数据地址,而实际对C++内存的访问是通过Unsafe来实现的。由于Java bytecode和Unsafe可以被JVM的JIT优化,因此JNI带来的性能瓶颈消失,极大的提升了Java对C++数据访问的性能。
成果:
- 框架实现:Alibaba FFI三大模块,即(1)用于开发Java bindings的DSL,(2)用于生成代码的代码生成器以及(2)以及将解决JNI性能瓶颈的LLVM4
- 性能评估:FFI对于在跨语言访问中性能由于既有的基于序列化框架的实现,该优势得到验证。
- 内部使用:Grape是阿里巴巴开源的图计算引擎,采用C++开发,其性能卓越。Grape团队成功的用Alibaba FFI给Grape开发了一套Java SDK,易用性和性能得到团队认可。
2015年加入蚂蚁金服, 参与会员、支付、金融网络等多个平台的系统建设。
2017年加入阿里巴巴 JVM 团队,目前主要负责 ServiceAbility 相关的工作,OpenJDK 8u Committer。
即将开始的GIAC课程
Alibaba FFI
JAVA进阶
2021-07-30 16:50--17:50
案例背景:
Java广泛应用于开发现代的大型软件系统,例如数据库,图计算引擎,大数据计算平台。虽然Java有着语法简单,性能卓越,内存安全等特性,但是其依赖垃圾回收自动管理内存,无法提供编程可控内存管理,在实际生产使用中往往遇到由于垃圾回到引起的难以解决的性能瓶颈。因此,一些Java实现的数据平台往往自身带有off-heap的支持,通过序列化等类似减少创建Java对象的数量。这种方式的坏处是丧失了面向对象开发范式的带来便捷,且开发offheap的支持易错,难以复用,以及序列化和反序列化带来的开销。
此外,一些数据平台往往采用本地语言,例如C、C++或者Rust,进行开发。虽然这些本地语言开发的平台有着高性能的特点,但是却难以推广。这是由于平台的使用者,也就是算法开发者,往往倾向于使用更为简单灵活的抽象语言,例如Java和Python。因此,这些本地语言开发的平台往往会提供Java的SDK供开发者使用,但不幸的是,这些SDK和宿主平台之间的数据交互往往是一个挑战。通过JNI来访问宿主平台的数据有着不可接收的额外负载,实际中这些平台往往采用一些跨平台内存数据格式(例如Apache Arrow和FlatBuffers)。然而,这些内存数据格式往往有一些限制,例如不可以增加或者插入数据。
解决思路:
我们认为(1)数据在内存中使用最灵活和性能最优异的格式或许是是以本地语言创建和访问的对象,而(2)本地语言或许最适合Java offheap方案的开发。
以C++为例,我们可以采用C++进行面向对象设计和开发,之后通过一些FFI框架复用C++数据结构来开发Java的SDK。这样,我们可以减少复杂的offheap方案开发,享受C++自由便利的内存管理。这个想法的困难在于两者:(1)开发FFI困难,直接使用JNI的开发枯燥且易错,采用既有的抽象FFI框架要么不支持C++,要么支持的不好(例如不支持C++模板到Java泛型的映射)。(2)Java的FFI框架只能直接或者间接的通过JNI来实现,而JNI的额外负载在频繁的数据访问非常显著,难以接受。
为了解决FFI开发的困难,我们基于Java的注解机制,开发了一套DSL,用于将C++的数据结构映射到Java世界。我们通过基于注解处理器的自动代码生成技术,来自动的将C++的数据结构映射到Java,用户只需要用Java语言,开发一些Java接口来实现映射即可。
为了解决FFI性能的问题,我们采用Clang+LLVM技术编译C++和JNI代码。之后,我们收集编译过程中得到的LLVM bitcode,将Java native方法对应的bitcode转换成Java bytecode,并替换对应的native方法为Java字节码方法。这些Java bytecode可以保留C++代码的计算逻辑,计算出需要访问的数据地址,而实际对C++内存的访问是通过Unsafe来实现的。由于Java bytecode和Unsafe可以被JVM的JIT优化,因此JNI带来的性能瓶颈消失,极大的提升了Java对C++数据访问的性能。
成果:
- 框架实现:Alibaba FFI三大模块,即(1)用于开发Java bindings的DSL,(2)用于生成代码的代码生成器以及(2)以及将解决JNI性能瓶颈的LLVM4
- 性能评估:FFI对于在跨语言访问中性能由于既有的基于序列化框架的实现,该优势得到验证。
- 内部使用:Grape是阿里巴巴开源的图计算引擎,采用C++开发,其性能卓越。Grape团队成功的用Alibaba FFI给Grape开发了一套Java SDK,易用性和性能得到团队认可。