网上的一些性能测试都比较久远了,还是自己动手丰衣足食吧。
测试环境
python3.7.1 + protobuffer3.7.1 + flatbuffer1.10.0
测试方式,对一个样本对象,压缩/解压 1000次。这个对象其实是flatbuffer的官方样例。
// Example IDL file for our monster's schema.
namespace MyGame.Sample;
enum Color:byte { Red = 0, Green, Blue = 2 }
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte];
color:Color = Blue;
weapons:[Weapon];
path:[Vec3];
}
table Weapon {
name:string;
damage:short;
}
root_type Monster;
对照着,我把它翻译为一个protobuffer的IDL文件
syntax = "proto3";
enum Color { Red = 0; Green=1; Blue = 2; }
message Vec3 {
float x=1;
float y=2;
float z=3;
}
message Monster {
Vec3 pos = 1; // Struct.
int32 mana = 2;
int32 hp = 3;
string name = 4;
bool friendly = 5 [deprecated=true];
repeated int32 inventory = 6; // Vector of scalars.
Color color = 7; // Enum.
repeated Weapon weapons = 8; // Vector of tables.
repeated Vec3 path = 9; // Vector of structs.
}
message Weapon {
string name = 1;
int32 damage = 2;
}
python模式下的protobuffer惨不忍睹
cpp模式下(set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp)的性能和flatbuffer基本是一个数量级。相比之下,pickle压缩后的串会比较长,虽然纯看压缩时间消耗,他们差别并不大。
这边顺带提一下,msgpack和thrift。msgpack没有IDL,而使用方法注册的方式定义解包/压包函数,个人觉得不是很友好。thrift是整套rpc+网络库+序列化的解决方案,过于庞大了,我们并不需要这些周边。另一方面,网上有资料显示thrift的性能与protobuf相当。
flatbuffer的读性能因为他的解包方式,产生了神奇的结果。部分读优势非常大,如果傻乎乎全解出来,性能就比protobuffer还要差了。所以,如果是和nosql缓存件配合使用,flatbuffer会是一个不错的选择,因为一般都只需要使用部分的字段就可以了。但是,如果是当rpc后端的序列化库使用,protobuffer明显更优,因为rpc往往需要完全解包。
那么只剩下flatbuffer和protobuffer了。
看下上面的IDL定义
flatbuffer是vb/js style的。protobuffer是c style的。个人喜好pb的声明方式一些。
fb的对象组装代码是最坑的,感觉萌萌嗒,有点像非常面向对象的结构代码,一大堆的字母
tpos = MyGame.Sample.Vec3.CreateVec3(builder, 1.0, 2.0, 3.0)
builder.PrependUOffsetTRelative(tpos)
paths = builder.EndVector(15)
pos = MyGame.Sample.Vec3.CreateVec3(builder, 1.0, 2.0, 3.0)
MyGame.Sample.Monster.MonsterStart(builder)
MyGame.Sample.Monster.MonsterAddPos(builder, pos)
MyGame.Sample.Monster.MonsterAddPath(builder, paths)
MyGame.Sample.Monster.MonsterAddHp(builder, 300)
MyGame.Sample.Monster.MonsterAddName(builder, name)
MyGame.Sample.Monster.MonsterAddInventory(builder, inv)
MyGame.Sample.Monster.MonsterAddColor(builder, MyGame.Sample.Color.Color().Red)
MyGame.Sample.Monster.MonsterAddWeapons(builder, weapons)
orc = MyGame.Sample.Monster.MonsterEnd(builder)
对比看下pb的代码,很简练,就和我们平时写代码来初始化对象一样
for i in range(20):
m.inventory.append(511)
m.inventory.append(5)
wp = Weapon()
wp.name='Sword'
wp.damage=3
m.weapons.append(wp)
wp = Weapon()
wp.name='Axe'
wp.damage=5
m.weapons.append(wp)
这点上,两种框架相差不大,功能都比较丰富,可以直接撸官网看
http://google.github.io/flatbuffers/flatbuffers_guide_tutorial.html
https://developers.google.com/protocol-buffers/docs/pythontutorial