简单介绍:
序列化框架是系统通信的基础组件,在大数据、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之类的框架没法处理重复引用和循环引用,这个功能主要是处理这个的,如果重复对象很多,这个还是有开销的,没重复对象引用建议关闭。