简单介绍:
 序列化框架是系统通信的基础组件,在大数据、AI 框架和云原生等分布式系统中广泛使用。当对象需要跨进程、跨语言、跨节点传输、持久化、状态读写、复制时,都需要进行序列化,其性能和易用性影响运行效率和开发效率。
 Fury 是一个基于 JIT 动态编译和零拷贝的多语言序列化框架,支持 Java/Python/Golang/JavaScript/C++ 等语言,提供全自动的对象多语言 / 跨语言序列化能力。
 而提到protostuff,就要先提到Protocol Buffer,它是谷歌出品的一种数据交换格式,独立于语言和平台,类似于json。Google提供了多种语言的实现:java、c++、go和python。对象序列化城Protocol Buffer之后可读性差,但是相比xml,json,它占用小,速度快。适合做数据存储或 RPC 数据交换格式,相对我们常用的json来说,Protocol Buffer门槛更高,因为需要编写.proto文件,再把它编译成目标语言,这样使用起来就很麻烦。但是现在有了protostuff之后,就不需要依赖.proto文件了,他可以直接对POJO进行序列化和反序列化,使用起来非常简单。
 今天,我们来做下性能评测:
 
fury
 官网:https://furyio.org
 开源地址:https://github.com/alipay/fury
 使用引入:
implementation 'org.furyio:fury-core:0.1.0-SNAPSHOT'
protostuff:
 官网:https://protostuff.github.io/
 开源地址:https://github.com/protostuff/protostuff
 使用引入:
implementation group: 'io.protostuff', name: 'protostuff-core', version: '1.8.0'
implementation group: 'io.protostuff', name: 'protostuff-runtime', version: '1.8.0'
测试设备: win11, 8core,16g memory,
 JDK:
openjdk version "11.0.16.1" 2022-08-16
OpenJDK Runtime Environment TencentKonaJDK (build 11.0.16.1+2)
OpenJDK 64-Bit Server VM TencentKonaJDK (build 11.0.16.1+2, mixed mode)
用游戏中高频调用的技能回包做样本,大小范围在512 bytes~1024 bytes,
SkillFire_S2C_Msg[attackerId=1833436122,harmList={HarmDTO[curHp=7557680.5,dead=true,maxHp=7276256.5,real=50382,targetId=1825720823,type=97,value=63549.6],HarmDTO[curHp=2986297.2,dead=true,maxHp=8404842.0,real=89296,targetId=1727245549,type=58,value=51803.74],HarmDTO[curHp=4064384.2,dead=true,maxHp=862263.3,real=11350,targetId=1337388443,type=4,value=6976.12],HarmDTO[curHp=3296058.5,dead=false,maxHp=2449570.5,real=74893,targetId=1757445513,type=44,value=37778.13],HarmDTO[curHp=6212545.0,dead=false,maxHp=7119135.0,real=38610,targetId=1724187257,type=41,value=30361.45],HarmDTO[curHp=5726974.5,dead=false,maxHp=7947759.5,real=1299,targetId=1450442553,type=94,value=69250.14],HarmDTO[curHp=4976733.5,dead=true,maxHp=9860293.0,real=46306,targetId=1324520518,type=2,value=54894.24],HarmDTO[curHp=8698692.0,dead=false,maxHp=3279770.8,real=55874,targetId=1554818116,type=88,value=69794.31],HarmDTO[curHp=5918141.5,dead=false,maxHp=158892.81,real=78468,targetId=1575461297,type=97,value=72174.01]},index=30,param1={2796842,9310196,2734093},skillCategory=ATTACKED_PASSIVE]
对fury和protobuff 从包的大小和吞吐量两个指标做了性能对比:
Benchmark                           Mode   Cnt     Score            Error  Units       bytes
ProtoBenchMark.furyDeserialize      thrpt    5      2599792.187 ± 251936.135  ops/s       776       
ProtoBenchMark.furySerialize         thrpt    5     3176333.674 ± 101602.041  ops/s       280
ProtoBenchMark.protostuffDeserialize  thrpt    5    185185.430   ± 35112.674    ops/s     776
ProtoBenchMark.protostuffSerialize     thrpt    5   150011.192   ±  72814.166    ops/s    141
图形对比:
 
 对序列化后传输包体大小的各种比较:
SkillFire_S2C_Msg[attackerId=1144770210,harmList={HarmDTO[curHp=7341064.5,dead=false,maxHp=1307891.2,real=39444,targetId=1705354437,type=84,value=5810.02],HarmDTO[curHp=2265639.2,dead=true,maxHp=3791511.8,real=65966,targetId=1234454096,type=49,value=12343.81],HarmDTO[curHp=217945.73,dead=false,maxHp=6192254.5,real=1516,targetId=1991499883,type=51,value=69098.27],HarmDTO[curHp=6802682.0,dead=false,maxHp=3503072.0,real=88600,targetId=1742534863,type=26,value=90365.62],HarmDTO[curHp=1498.77,dead=true,maxHp=1320275.8,real=88398,targetId=1033631343,type=90,value=5265.25],HarmDTO[curHp=4550666.5,dead=true,maxHp=3345442.2,real=20653,targetId=2059955709,type=25,value=89742.31]},index=21,param1={5251655,4692175,1099790,2861,8402466},skillCategory=ATTRIBUTE_ATTRIBUTE]
共 912 bytes,
| 协议 | 设置 | 压缩率 | 
|---|---|---|
| fury | RefTracking=true, Number Compress=false | 42.87% | 
| fury | RefTracking=true, Number Compress=true | 32.68% | 
| fury | RefTracking=false, Number Compress=true | 32.68% | 
| fury | RefTracking=false, Number Compress=true,class register | 25.66% | 
| fury | RefTracking=true, Number Compress=true,class register | 25.66% | 
| Protostuff | 23.79% | 
在引用解析(RefTracking)关闭,类注册 (ClassRegistration)打开,整数压缩(NumberCompressed)打开的情况下,我们再把这个纳入到性能测试案例中,得到了如下的数据(见enhance结尾的数据):
Benchmark                               Mode  Cnt        Score        Error  Units
ProtoBenchMark.furyDeserialize         thrpt    5  4178383.458 ± 125283.184  ops/s
ProtoBenchMark.furyDeserializeEnhance  thrpt    5  2982546.234 ± 311905.075  ops/s
ProtoBenchMark.furySerialize           thrpt    5  2675549.131 ± 117827.214  ops/s
ProtoBenchMark.furySerializeEnhance    thrpt    5  5063335.687 ± 166255.830  ops/s
ProtoBenchMark.protostuffDeserialize   thrpt    5   174876.485 ±  10882.585  ops/s
ProtoBenchMark.protostuffSerialize     thrpt    5   205167.058 ±  12125.220  ops/s

结论:
 吞吐量对比,
 在默认引用解析打开,类注册关闭,整数压缩关闭的情况下,序列化上,fury 是protostuff 13 倍,反序列化上 fury 是protostuff 的23.89倍,完胜!
 在引用解析关闭,类注册打开,整数压缩打开的情况下,序列化上,fury 提高到了protostuff 24.68 倍,反序列化上 fury 降低到了protostuff 的17.06倍,这个配置策略更适合游戏服务器!
 包体压缩比上,在默认情况下,fury 和 protostuff 比较 42.87%> 23.79% , 大了快一倍左右,但在开启整数压缩和类名称注册下,压缩效果明显,达到了25.66%,基本已经接近Protostuff的压缩率。
 但奇怪的是,引用解析(RefTracking)关闭和开启,对结果影响不大。
 从官方问来了答案:
引用解析(RefTracking):pb之类的框架没法处理重复引用和循环引用,这个功能主要是处理这个的,如果重复对象很多,这个还是有开销的,没重复对象引用建议关闭。