序列化流量及CPU负载对比

藏书馆

欢迎来到网易游戏学院藏书馆

序列化流量及CPU负载对比

作者:甘洁(程序)

  BSON序列化产生的流量比较多,CPU占用也比较高,而JSON是一种轻量级的数据结构,比较简洁,可以作为BSON的替代。protobuf是常用的序列化方式,其与BSON,JSON各有优劣。本文主要从序列化流量和CPU占用两方面对比BSON、JSON、protobuf三种方式,希望找出较好的一种方式。 

  最近由于项目需求,需要对游戏实时对战模块的数据传输部分进行优化,主要是针对数据包encode和decode的CPU消耗以及产生的流量进行优化。优化前的实现方式:服务端用BSON对RPC参数进行encode,客户端用BSON对RPC参数进行decode。由于用BSON对数据进行encode会记录下一些额外的结构信息,因此猜测encode出来的字节流应该是有冗余,相比之下,JSON的格式比较简单,可能能够更节省流量,所以考虑比较一下JSON和BSON在流量产生上的区别。另外,由于JSON是一种字符串格式,所以还可以使用python自带的zlib库对JSON字符串再次压缩,以达到节省流量的目的。当然,我们的目标是从流量和CPU负载上对原有方案进行优化,因此在使用JSON进行序列化的时候,还得比较一下CPU负载状况,以免出现顾此失彼的情况。此外,protobuf也是一种常用的序列化解决方案,因此比较的对象就定为BSON、JSON、以及protobuf。

  测试平台:windows7

  python 版本:2.7.9

  mongodb版本:2.4.8

  我们先比较一下BSON和JSON。以下是简要的实验步骤和一些实验数据(以下实验中,参与对战的双方一直保持不变):

  (1)首先计算一下原方案下BSON所产生的流量状况。做法是在一场实时战斗过程中,统计所有RPC调用的次数以及总计发送字节数,根据这两个值估计平均每次RPC调用发送的字节数。结果如下:

  平均每次RPC调用产生316字节的流量。

  (2)计算一下服务端改用JSON进行encode时平均每个RPC调用字节数,结果如下:

  平均每次RPC调用产生195字节流量。

  综合(1)和(2)可以看出,BSON方式比JSON方式的流量消耗高了60%以上.

  (3)上面的实验表明将BSON换成JSON可以比较明显地节约流量。那么,更进一步,将JSON格式的数据经过zlib压缩后,效果会不会更好呢?以下是将JSON字符串进一步用zlib压缩后的结果(zlib的compress level设为1):

  可以看到,流量又有进一步的改善,尚不到BSON方案的一半。

  (4)JSON+zlib已经被证明能有效改善流量,那么,采用更高的zlib压缩级别能不能改善得更多呢?直觉上觉得不能~~~实验结果如下:

  可以看到,确实如猜想,将zlib的压缩级别调到最高并不能明显改善流量,并且之后做了compress level分别为1和9的情况下CPU占用的测试,压缩级别为9时CPU的占用时间显著变长,因此压缩级别设为1是比较好的选择,同时兼顾了流量和CPU负载。

  至此,可以总结出JSON+zlib compress level 1的组合在流量的优化上有比较好的效果。接下来考虑上述方案的CPU占用情况。做法与流量类似,就是统计所有RPC调用的次数和进行JSON encode和zlib压缩所需要的总时间,计算出平均每个RPC调用参数序列化所需的时间。

  (5)先看一下BSON方案的CPU时间:

  可以看到,平均每个RPC调用中参数encode的时间是0.000382秒

  (6)再对比一下只进行JSON encode(不包括zlib压缩)的CPU时间:

  可以看到,比BSON好了很多,只相当于原来的12%左右。

  (7)在(6)的基础上加上zlib(compress level = 1)的时间,结果如下:

  可以看出,zlib压缩所占用的CPU时间也并不多,JSON+zlib整体的CPU占用时间对比于BSON仍然有质的优势。

  从上述实验结果来看,JSON + zlib的组合明显优于BSON。

  下面测一下protobuf的情况。说到protobuf,首先一定会想到它的高压缩比,所以期望是它的结果会比JSON要好。(由于以下实验是第二天做的,所以实验样本跟之前的不一样,不过只求定性分析,所以也不必太精确)

  (8)将RPC参数序列化的方法改为protobuf,流量的统计结果如下:

  可以看到,压缩率确实比纯json encode的要高,但是并不如json + zlib组合

  (9)那么,protobuf + zlib(complress level 1),会不会效果拔群?以下是实验结果:

  其实并没有比json + zlib 好太多,几乎可以忽略不计

  (10)那么,在CPU占用上,protobuf的表现如何呢?如下图:

  图中采用的zlib 的 compress level 为1,可以看到时间上比BSON方案多了一个数量级,更不用说跟JSON+zlib的方案进行比较。其中比较消耗的部分是将python类型转化为protobuf类型,由于是采用python递归的方式进行,因此效率不高,如果采用这个方案,将这部分改为C++应该能取得比较好的效果,但是整体而言并不如使用JSON+zlib。

  综合以上实验,得出结论:使用JSON+zlib对数据进行序列化时从流量上和CPU占用量上都比BSON有优势。G4的实时对战目前已经改为json方案,从外服的服务器统计数据来看,确实比原来要好。

  补充:BSON在序列化上之所以比JSON要慢,主要是由于BSON牺牲了一部分序列化效率来换取更佳的反序列化效率(比如:在字段头部增加4字节记录字段总长度等)。在数据的更新和查找上,BSON具有比JSON更高的效率,这也是mongodb采用BSON作为数据存储格式的原因之一。因此,对于不同的应用领域,要综合考虑各种因素的影响,切不可一刀切。