阿里alipay的Fury是一个极快的多语言序列化框架,由jit(即时编译)和零拷贝提供支持,提供高达 170 倍的性能和终极易用性。
仅用于序列化
通过使用fury将Java对象转换为字节流,您可以获得高达170倍的速度提升。并非所有情况都能获得 170 倍的加速。
170x 仅适用于这个结构:所有字段都是数字字段。
- https://github.com/eishay/jvm-serializers/blob/master/tpc/src/data/media/MediaContent.java中的 MediaContent 类,fury 的反序列化速度比 JDK 快 52 倍,序列化速度比 JDK 快 22 倍。
- https://github.com/EsotericSoftware/kryo/blob/master/benchmarks/src/main/java/com/esotericsoftware/kryo/benchmarks/data/Sample.java中的示例类,fury 的反序列化速度比 jdk 快 71 倍,序列化速度比 JDK 快 30 倍。
字符串、原始数组序列化将需要一个副本,并且会稍微摊销由 Fury jit 带来的加速。
序列化
常见的序列化操作包括:
- 位图操作
- 数字编码/解码
- int/long 压缩
- 字符串创建/复制
- 字符串编码:ASCII/UTF8/UTF16
- 内存复制
- 数组复制&压缩
- 元编码&压缩&缓存
Fury 使用 SIMD 和其他高级语言功能使每种语言的基本操作都变得非常快。
JIT动态编译加速
自定义类型对象通常包含大量类型信息,Fury利用这些信息在运行时生成高效的序列化代码,从而可以将大量运行时操作推入动态编译阶段。通过内联更多的方法、更好的代码缓存、减少虚拟方法调用、条件分支、哈希查找、元数据写入和内存读/写,序列化性能大大加快。
对于 Java,Fury 实现了运行时代码生成框架并定义了运算符表达式 IR。然后Fury可以在运行时根据对象的泛型类型信息进行类型推断,构建描述序列化代码逻辑的表达式树。codegen框架将从表达式树生成高效的Java代码,然后传递给Janino将其编译为字节码,并加载到用户的ClassLoader或Fury创建的ClassLoader中,最后通过Java JIT将其编译为高效的汇编代码。
由于JVM JIT跳过了大方法的编译和内联,Fury还实现了一个优化器,将大方法递归地拆分为小方法,从而确保所有代码都可以编译和内联。
小结:
- 通过生成代码中的内联变量减少内存访问。
- 通过生成代码中的内联调用减少虚拟方法调用。
- 减少条件分支。
- 减少哈希查找。所有内存访问、检查、虚拟方法
通过这种技术,序列化会快得多。唯一成本消耗只是内存复制。
静态代码生成
虽然JIT编译可以极大地提高序列化效率,并根据运行时数据的统计分布生成更好的序列化代码,但像这样的语言C++不支持反射,没有虚拟机,也没有内存模型的低级API。我们无法通过 JIT 为此类语言动态生成序列化代码。
在这种场景下,Fury正在实现一个AOT codegen框架,它根据对象模式静态生成序列化代码,并且可以使用生成的序列化器自动序列化对象。对于 Rust,Rust 宏用于静态生成代码。
缓存优化
在序列化自定义类型时,fury 会重新排序字段,以确保相同类型的字段按顺序序列化。这样可以命中更多的数据缓存和CPU指令缓存。
基本类型字段按字节大小降序写入。这样,如果初始地址对齐,后续的读写操作就会发生在内存地址对齐的位置,使得CPU执行效率更高。
Java序列化
Java 广泛应用于大数据、云原生、微服务和企业应用程序。因此,Fury对Java序列化做了很多优化,大大降低了系统延迟和服务器成本,并显着提高了吞吐量。我们的实施有以下亮点:
- 极快的性能:基于 Java 类型、JIT 编译和 Unsafe 低级操作,Fury 比 JDK 快 170 倍,最多比 Kryo/Hessian 快 50~110 倍。
- 100% JDK序列化API兼容性:原生支持所有JDK自定义序列化方法writeObject/readObject/writeReplace/readResolve/readObjectNoData,保证任何场景下序列化的正确性。Kryo/Hessian 在这些场景中存在一些正确性问题。
- 直接替换JDK/Kryo/Hessian/Fst,无需修改用户代码。
- 类型向前/向后兼容性:当反序列化和序列化类schema不一致时,仍然可以正确反序列化。支持应用程序升级和部署,独立添加/删除字段。与类型一致模式相比,Fury 类型兼容模式的实现没有性能损失。
- 元数据共享:在上下文(TCP连接)下跨多个序列化共享元数据(类名,字段名和类型等),元数据仅在第一次序列化时发送给对等方,对等方可以根据此信息重建相同的反序列化器。后续序列化将跳过传输元数据,这样可以减少网络流量,并自动支持类型兼容性。
- 零拷贝支持:支持带外零拷贝和堆外内存读/写。
Fury 支持所有通过 writeObject/readObject/writeReplace/readResolve/readObjectNoData/Externalizable 定制的序列化。用户不必更改这些代码。
项目点击标题