C轮融资|Fury:一个基于JIT动态编译的高性能多语言原生序列化框架( 八 )


public final class Media implements java.io.Serializable { public String uri; public String title; // Can be null. public int width; public int height; public String format; public long duration; public long size; public int bitrate; public boolean hasBitrate; public ListStringpersons; public Player player; public String copyright; // Can be null. public Media() { public enum Player { JAVA FLASH; public final class MediaContent implements java.io.Serializable { public Media media; public ListImageimages; public MediaContent() { public MediaContent(Media media ListImageimages) { this.media = media; this.images = images; public MediaContent populate(boolean circularReference) { media = new Media(); media.uri = \"http://javaone.com/keynote.ogg\"; media.width = 641; media.height = 481; media.format = \"video/theora\\u1234\"; media.duration = 18000001; media.size = 58982401; media.persons = new ArrayList(); media.persons.add(\"Bill Gates Jr.\"); media.persons.add(\"Steven Jobs\"); media.player = Media.Player.FLASH; media.copyright = \"Copyright (c) 2009 Scooby Dooby Doo\"; images = new ArrayList(); Media media = circularReference ? this.media : null; images.add( new Image( \"http://javaone.com/keynote_huge.jpg\" \"Javaone Keynote\\u1234\" 32000 24000 Image.Size.LARGE media)); images.add( new Image( \"http://javaone.com/keynote_large.jpg\" null 1024 768 Image.Size.LARGE media)); images.add( new Image(\"http://javaone.com/keynote_small.jpg\" null 320 240 Image.Size.SMALL media)); return this;序列化耗时:


反序列化耗时:


Buffer零拷贝性能对比
基本类型数组
对于基本类型可以看到Fury序列化几乎耗时为0 , 而别的框架耗时随着数组大小线性增加 。
反序列时Fury耗时也会线性增加是因为需要把Buffer拷贝到Java基本类型数组里面 。
public class ArraysData implements Serializable { public boolean[
booleans; public byte[
bytes; public int[
ints; public long[
longs; public double[
doubles; public ArraysData() { public ArraysData(int arrLength) { booleans = new boolean[arrLength
; bytes = new byte[arrLength
; ints = new int[arrLength
; longs = new long[arrLength
; doubles = new double[arrLength
; Random random = new Random(); random.nextBytes(bytes); for (int i = 0; iarrLength; i++) { booleans[i
= random.nextBoolean(); ints[i
= random.nextInt(); longs[i
= random.nextLong(); doubles[i
= random.nextDouble();序列化耗时:


反序列耗时:


堆外Buffer
除了基本类型数组 , 我们也测试了Java ByteBuffer的序列化性能 。 由于Kryo和Fst并不支持ByteBuffer序列化 , 同时并没有提供直接读写ByteBuffer的接口 , 因此我们使用了byte array来模拟内存拷贝 。 可以看到对于堆外Buffer , Fury的序列化和反序列化耗时都是一个常量 , 不随Buffer大小而增加 。
序列化耗时:


反序列化耗时:


易用性比较
这里以一个自定义类型为例对比易用性 , 该类型包含常见基本类型字段以及集合类型字段 , 最终需要序列化的对象是一个Bar的实例:
class Foo { String f1; MapString Integerf2;class Bar { Foo f1; String f2; ListFoof3; MapInteger Foof4; Integer f5; Long f6; Float f7; Double f8; short[
f9; ListLongf10; Fury序列化
Fury序列化只需一行代码 , 且无任何学习成本 。
Fury fury = Fury.builder().withLanguage(Language.XLANG).build();byte[
data = https://mparticle.uc.cn/api/fury.serialize(bar);// 这里的data可以是被Fury python/Golang实现序列化的数据Bar newBar = fury.deserialize(data); 对比Protobuf
首先需要安装protoc编译器[10
, 注意protoc的版本不能高于proto依赖库的版本 然后定义针对需要序列化的对象的schema: syntax = \"proto3\";package protobuf;option java_package = \"io.ray.fury.benchmark.state.generated\";option java_outer_classname = \"ProtoMessage\";message Foo { optional string f1 = 1; mapstring int32f2 = 2;message Bar { optional Foo f1 = 1; optional string f2 = 2; repeated Foo f3 = 3; mapint32 Foof4 = 4; optional int32 f5 = 5; optional int64 f6 = 6; optional float f7 = 7; optional double f8 = 8; repeated int32 f9 = 9; // proto不支持int16 repeated int64 f10 = 10; 然后通过protoc编译schema生成Java/Python/GoLang代码文件 。java: protoc --experimental_allow_proto3_optional -I=src/main/java/io/ray/fury/benchmark/state --java_out=src/main/java/ bench.proto bench.proto 生成Python/GoLang代码 为了避免把生成的代码提交到代码仓库 , 需要将proto跟构建工具进行集成 , 这块较为复杂 , 存在大量构建工具集成成本 。 且由于构建工具的不完善 , 这部分依然无法完全自动化 , 比如protobuf-maven-plugin[11