pickle, flatbuffer与protobuffer对比

测试方法

网上的一些性能测试都比较久远了,还是自己动手丰衣足食吧。

测试环境

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相当。

pickle, flatbuffer与protobuffer对比_第1张图片

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

  • 都支持很多种语言的导出,主流的java, C, python一般都支持
  • 支持各种类型,以及数组等
  • 可选字段和必选字段
  • 与json的转换
  • 自定义扩展的标记

 

你可能感兴趣的:(python,网络,游戏)