一、引言
本系列的前几篇主要是从各个角度讲解Protobuf的基本概念、技术原理这些内容,但回过头来看,对比JSON这种事实上的数据协议工业标准,Protobuf到底性能到底高多少?
本篇将以Protobuf为基准,对比市面上的一些主流的JSON解析库,通过全方位测试来证明给你看看Protobuf到底比JSON快几倍。
二、本文目录
三、系列文章
本文是系列文章中的第 4 篇,本系列总目录如下:
《IM通讯协议专题学习(五):Protobuf到底比JSON快几倍?全方位实测!》(* 本文)
《IM通讯协议专题学习(六):手把手教你如何在Android上从零使用Protobuf》(稍后发布..)
《IM通讯协议专题学习(七):手把手教你如何在NodeJS中从零使用Protobuf》(稍后发布..)
《IM通讯协议专题学习(八):金蝶随手记团队的Protobuf应用实践(原理篇) 》(稍后发布..)
《IM通讯协议专题学习(九):金蝶随手记团队的Protobuf应用实践(实战篇) 》(稍后发布..)
四、写在前面
拿 JSON 衬托 Protobuf 的文章真的太多了,经常可以看到文章中写道:“快来用 Protobuf 吧,JSON 太慢啦”。
但是 Protobuf 真的有吹的那么牛么?
我觉得从 JSON 切换到 Protobuf 怎么也得快一倍吧,要不然对不起付出的切换成本。然而,DSL-JSON 的家伙们居然说在Java语言里 JSON 和那些二进制的编解码格式有得一拼,这太让人惊讶了!
虽然你可能会说,咱们能不用苹果和梨来做比较了么?两个东西根本用途完全不一样好么。咱们用 Protobuf 是冲着跨语言无歧义的 IDL 的去的,才不仅仅是因为性能呢。好吧,这个我同意。但是仍然有那么多人盲目相信,Protobuf 一定会快很多,我觉得还是有必要彻底终结一下这个关于速度的传说。
DSL-JSON 的博客里只给了他们的测试结论,但是没有给出任何原因,以及优化的细节,这很难让人信服数据是真实的。你要说 JSON 比二进制格式更快,真的是很反直觉的事情。
稍微琢磨一下这个问题,就可以列出好几个 Protobuf 应该更快的理由。
比如:
1)更容容易绑定值到对象的字段上。JSON 的字段是用字符串指定的,相比之下字符串比对应该比基于数字的字段tag更耗时;
2)JSON 是文本的格式,整数和浮点数应该更占空间而且更费时;
3)Protobuf 在正文前有一个大小或者长度的标记,而 JSON 必须全文扫描无法跳过不需要的字段。
但是仅凭这几点是不是就可以盖棺定论了呢?未必。
也有相反的观点:
1)如果字段大部分是字符串,占到决定性因素的因素可能是字符串拷贝的速度,而不是解析的速度。在这个评测中,我们看到不少库的性能是非常接近的。这是因为测试数据中大部分是由字符串构成的;
2)影响解析速度的决定性因素是分支的数量。因为分支的存在,解析仍然是一个本质上串行的过程。虽然Protobuf里没有[] 或者 {},但是仍然有类似的分支代码的存在。如果没有这些分支的存在,解析不过就是一个 memcpy 的操作而已。只有 Parabix 这样的技术才有革命性的意义,而 Protobuf 相比 JSON 只是改良而非革命;
3)也许 Protobuf 是一个理论上更快的格式,但是实现它的库并不一定就更快。这取决于优化做得好不好,如果有不必要的内存分配或者重复读取,实际的速度未必就快。
有多个 benchmark 都把 DSL-JSON列到前三名里,有时甚至比其他的二进制编码更快。
经过我仔细分析,原因出在了这些 benchmark 对于测试数据的构成选择上。
因为构造测试数据很麻烦,所以一般评测只会对相同的测试数据,去测不同的库的实现。
这样就使得结果是严重倾向于某种类型输入的。
比如 https://github.com/eishay/jvm-serializers/wiki 选择的测试数据的结构是这样的:
| 01020304050607080910111213141516171819202122232425262728293031323334 | message Image { required string uri = 1; //url to the thumbnail optional string title = 2; //used in the html ALT required int32 width = 3; // of the image required int32 height = 4; // of the image enum Size { SMALL = 0; LARGE = 1; } required Size size = 5; // of the image (in relative terms, provided by cnbc for example)} message Media { required string uri = 1; //uri to the video, may not be an actual URL optional string title = 2; //used in the html ALT required int32 width = 3; // of the video required int32 height = 4; // of the video required string format = 5; //avi, jpg, youtube, cnbc, audio/mpeg formats ... required int64 duration = 6; //time in miliseconds required int64 size = 7; //file size optional int32 bitrate = 8; //video repeated string person = 9; //name of a person featured in the video enum Player { JAVA = 0; FLASH = 1; } required Player player = 10; //in case of a player specific media optional string copyright = 11;//media copyright} message MediaContent { repeated Image image = 1; required Media media = 2;} |
无论怎么去构造 small/medium/large 的输入,benchmark 仍然是存在特定倾向性的。
而且这种倾向性是不明确的。比如 medium 的输入,到底说明了什么?medium 对于不同的人来说,可能意味着完全不同的东西。
所以,在这里我想改变一下游戏的规则。不去选择一个所谓的最现实的配比,而是构造一些极端的情况。
这样,我们可以一目了然的知道,JSON的强项和弱点都是什么。通过把这些缺陷放大出来,我们也就可以对最坏的情况有一个清晰的预期。具体在你的场景下性能差距是怎样的一个区间内,也可以大概预估出来。
五、本次评对象
好了,废话不多说了,JMH 撸起来。
benchmark 的对象有以下几个:
1)Jackson:Java 程序里用的最多的 JSON 解析器。benchmark 中开启了 AfterBurner 的加速特性;
2)DSL-JSON:世界上最快的 Java JSON 实现;
3)Jsoniter:抄袭 DSL-JSON 写的实现;
4)Fastjson:在中国很流行的 JSON 解析器;
5)Protobuf:在 RPC (远程方法调用)里非常流行的二进制编解码格式;
6)Thrift:另外一个很流行的 RPC 编解码格式。这里 benchmark 的是 TCompactProtocol。
六、参考资料
http://www.52im.net/thread-4080-1-1.html
七、全文链接
即时通讯网(52im.net)社区链接:http://www.52im.net/thread-4095-1-1.html,或点击下文的“阅读原文”!