为什么专门开一个坑,来使用pb。放弃本在各平台上都支持得很好的json而使用pb的一个归根到底的理由,就是希望在保证强类型和跨平台的情况下,能够更轻,更快,更简单。既然是奔着这个目标去的,到底多快我需要一个合理的解释。
在使用pure python官方库的的情况下,对比了pb和json标准库,还有simplejson库的速度。
使用的.proto文件文件如下:
syntax = "proto2"; package hello_word; message SayHi { required int32 id = 1; required string something = 2; optional string extra_info = 3; }
python文件可以根据这个生成对应的SayHi obejct。
测试各库序列化速度的代码如下所示:
# coding: utf-8 import timeit # 序列化 x = """ say_hi.SerializeToString() """ y = """ json.dumps(ppa) """ z = """ simplejson.dumps(pl) """ print min(timeit.repeat(stmt=x, setup="import say_hi_pb2;" "say_hi = say_hi_pb2.SayHi();" "say_hi.id = 13423;" "say_hi.something = ‘axiba‘;" "say_hi.extra_info = ‘xiba‘;", repeat=5, number=100000)) print min(timeit.repeat(stmt=y, setup="import json; " "ppa={" "‘id‘: 13423," "‘something‘: ‘axiba‘," "‘extra_info‘: ‘xiba‘," "};", repeat=5, number=100000)) print min(timeit.repeat(stmt=z, setup="import simplejson; " "pl={" "‘id‘: 13423," "‘something‘: ‘axiba‘," "‘extra_info‘: ‘xiba‘," "};", repeat=5, number=100000)) 输出:
1.08438277245
0.398800134659
0.707333087921
测试各库反序列化速度的代码如下所示:
# coding: utf-8 import timeit # 反序列化 x = """ say_hi.ParseFromString(p) """ y = """ json.loads(p1) """ z = """ simplejson.loads(p2) """ print min(timeit.repeat(stmt=x, setup="import say_hi_pb2;" "say_hi = say_hi_pb2.SayHi();" "say_hi.id = 13423;" "say_hi.something = ‘axiba‘;" "say_hi.extra_info = ‘xiba‘;" "p = say_hi.SerializeToString()", repeat=5, number=100000)) print min(timeit.repeat(stmt=y, setup="import json; " "ppa={" "‘id‘: 13423," "‘something‘: ‘axiba‘," "‘extra_info‘: ‘xiba‘," "};" "p1 = json.dumps(ppa)", repeat=5, number=100000)) print min(timeit.repeat(stmt=z, setup="import simplejson; " "pl={" "‘id‘: 13423," "‘something‘: ‘axiba‘," "‘extra_info‘: ‘xiba‘," "};" "p2 = simplejson.dumps(pl)", repeat=5, number=100000))输出:
0.924090862274
0.492631912231
0.283575057983
从上面的数据可以看出,在我使用的版本3.1.0.post1的情况下,纯python实现pb序列化的速度略慢于json原生库两倍多,比simplejson库慢百分之30。在反序列化的速度测试中,依然是pb速度最慢两倍慢于原生json库,慢于simplejson库3倍多。这样看起来差距似乎被优化得不那么大了。记得以前在使用pb2.x库的时候,python序列化常慢于simplejson 3倍以上是非常正常的事情。各分析性能的文章都可以看到 too slow这个描述。由于二进制存储,以及pb独特的编码二进制的方式,从大小的角度来说,pb远远小于json,但是速度连json都快不过,我们有什么理由放弃使用方便可依赖的json转而使用pb呢?这的确没有什么说服力。
然而,pb官方提供了一个c++实现 runtime for python,按照实践一中的方法,安装好最新的pb库,并且按照文档编译好,然后安装python 的c++实现,就可以让pb使用c++实现进行序列化反序列。其他生成代码之类的所有不用变,调用代码也不用变,只需要安装好就可以了。安装好之后可以看到
Using /Users/piperck/Desktop/grpc/lib/python2.7/site-packages
Finished processing dependencies for protobuf==3.1.0
再次使用pip list查看我们的pb的时候可以发现,已经被该库替代。
让我们来重新运行一下 序列化和反序列化的代码:
序列化输出: 0.085785150528 0.403172016144 0.755691051483 反序列化输出: 0.090231180191 0.499733924866 0.297739028931
可以看到几乎比pure python的实现快近10倍。如果把序列化和反序列按照一次计算进行计算的话,也比我们通常使用的simplejson库快上4到5倍。再频繁调用序列化反序列化的应用中,可以说还是比较大的性能提升了,可以使得你的代码更轻更快,而且强类型映射可以检查错误。
别以为到这里就完了。还有一个更快速的库,但是现在只支持proto2,叫Pyrobuf Library。基于cPython实现,根据作者的说法,他要比c++ backend for python 还要快上2-4倍。这个由于我安装了半天没有安装上,貌似是cPython库的配置稍为有点问题,如果大家对速度有更快的要求,可以查看reference给出的第二个链接,去探索一下。
Reference:
https://github.com/google/protobuf/tree/master/python pb-github库
https://github.com/appnexus/pyrobuf Pyrobuf Library
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 32.0px "Helvetica Neue"; color: #333333; background-color: #ffffff }
span.s1 { }