Protobuf有没有比JSON快5倍?用代码来击破pb性能神话

导读:Google 的 Protocol Buffers 在数据编码的效率上似乎被神化了,一直流传性能是 JSON 等本文格式 5 倍以上,本文通过代码测试来比较 JSON 与 PB 具体的性能差别到底是多少。作者陶文,转载请注明来自高可用架构「ArchNotes」

0?wx_fmt=png陶文,技术极简主义者。认为好的技术是应该是对开发者友好的。一直致力于用技术改进研发效率和开发者体验。jsoniter [4] 作者,jsoniter 就来自于要不要用 Thrift 替代 JSON 的思考。我认为通过引入 IDL 和高效率的编解码库,可以让 HTTP + JSON 这样对开发者体验有好处的技术长久地生存下去。

拿 JSON 衬托 Protobuf 的文章真的太多了,经常可以看到文章中写道:“快来用 Protobuf 吧,JSON 太慢啦”。但是 Protobuf 真的有那么牛么?我想从 JSON 切换到 Protobuf 怎么也得快一倍吧,要不然对不起付出的切换成本?

然而,DSL-JSON 居然声称在 Java 语言里, JSON 可以和那些二进制的编解码格式性能不相上下 [1]!

这太让人惊讶了!虽然你可能会说,咱们能不用苹果和梨来做比较了么,两个东西根本用途完全不一样,用 Protobuf 是冲着跨语言无歧义的 IDL 的去的,才不仅仅是因为性能!这个我同意,但是仍然有那么多人盲目相信 Protobuf 一定会快很多,因此我觉得还是有必要通过本文彻底终结一下这个关于速度的传说。

DSL-JSON 的博客里只给了他们的测试结论,但是没有给出任何原因,以及优化的细节。这很难让人信服数据是真实的。你要说 JSON 比二进制格式更快,真的是很反直觉的事情。稍微琢磨一下这个问题,就可以列出好几个 Protobuf 应该更快的理由:


  • 更容容易绑定值到对象的字段上。JSON 的字段是用字符串指定的,相比之下字符串比对应该比基于数字的字段 tag 更耗时。

  • JSON 是文本的格式,整数和浮点数应该更占空间而且更费时。

  • Protobuf 在正文前有一个大小或者长度的标记,而 JSON 必须全文扫描无法跳过不需要的字段。


但是仅凭这几点是不是就可以盖棺定论了呢?未必,也有相反的观点:


  • 如果字段大部分是字符串,占到决定性因素的因素可能是字符串拷贝的速度,而不是解析的速度。在这个评测 [2] 中,我们看到不少库的性能是非常接近的。这是因为测试数据中大部分是由字符串构成的。

  • 影响解析速度的决定性因素是分支的数量。因为分支的存在,解析仍然是一个本质上串行的过程。虽然 Protobuf 里没有 [] 或者 {},但是仍然有类似的分支代码的存在。如果没有这些分支的存在,解析不过就是一个 memcpy 的操作而已。只有 Parabix 这样的技术才有革命性的意义,而 Protobuf 相比 JSON 只是改良而非革命。

  • 也许 Protobuf 是一个理论上更快的格式,但是实现它的库并不一定就更快。这取决于优化做得好不好,如果有不必要的内存分配或者重复读取,实际的速度未必就快。

有多个 benchmark 都把 DSL-JSON 列到前三名里,有时甚至比其他的二进制编码更快。经过我仔细分析,原因出在了这些 benchmark 对于测试数据的构成选择上。因为构造测试数据很麻烦,所以一般评测只会对相同的测试数据,去测不同的库的实现。这样就使得结果是严重倾向于某种类型输入的。比如 [3] 选择的测试数据的结构是这样的

0?wx_fmt=png

点击图片可以放大浏览

无论怎么去构造 small/medium/large 的输入,benchmark 仍然是存在特定倾向性的。而且这种倾向性是不明确的。比如 medium 的输入,到底说明了什么?medium 对于不同的人来说,可能意味着完全不同的东西。所以,在这里我想改变一下游戏的规则。不去选择一个所谓的最现实的配比,而是构造一些极端的情况。这样,我们可以一目了然的知道,JSON 的强项和弱点都是什么。通过把这些缺陷放大出来,我们也就可以对最坏的情况有一个清晰的预期。具体在你的场景下性能差距是怎样的一个区间内,也可以大概预估出来。


好了,废话不多说了。JMH 撸起来。benchmark 的对象有以下几个:


  • Jackson:https://github.com/FasterXML/jackson-databind Java 程序里用的最多的 JSON 解析器。benchmark 中开启了 AfterBurner 的加速特性。

  • DSL-JSON:https://github.com/ngs-doo/dsl-json 世界上最快的 Java JSON 实现

  • Jsoniter: http://jsoniter.com/index.cn.html  我抄袭 DSL-JSON 写的实现。特别申明:我是 Jsoniter 的作者。这里提到的所有关于Jsoniter 的评测数据都不应该被盲目相信。大部分的性能优化技巧是从 DSL-JSON 中直接抄来的。

  • Fastjson:https://github.com/alibaba/fastjson 在中国很流行的 JSON 解析器

  • Protobuf:https://github.com/google/protobuf 在 RPC (远程方法调用)里非常流行的二进制编解码格式

  • Thrift:https://thrift.apache.org 另外一个很流行的 RPC 编解码格式。这里 benchmark 的是 TCompactProtocol


Decode Integer


先从一个简单的场景入手。毫无疑问,Protobuf 非常擅长于处理整数

0?wx_fmt=png

https://github.com/json-iterator/java-benchmark/tree/master/src/main/java/com/jsoniter/benchmark/with_int

相比 Jackson ns/op
Protobuf 8.20 22124.431
Thrift 6.6 27232.761
Jsoniter 6.45 28131.009
DSL-JSON 4.48 40472.032
Fastjson 2.1 86555.965
Jackson 1 181357.349

从结果上看,似乎优势非常明显。但是因为只有 1 个整数字段,所以可能整数解析的成本没有占到大头。所以,我们把测试调整对象调整为 10 个整数字段。再比比看


0?wx_fmt=png

点击图片可以放大浏览


https://github.com/json-iterator/java-benchmark/tree/master/src/main/java/com/jsoniter/benchmark/with_10_int_fields

相比 Jackson ns/op
Protobuf 8.51 71067.990
Thrift 2.98 202921.616
Jsoniter 3.22 187654.012
DSL-JSON 1.43 422839.151
Fastjson 1.4 432494.654
Jackson 1 604894.752

这下优势就非常明显了。毫无疑问,Protobuf 解析整数的速度是非常快的,能够达到 Jackson 的 8 倍


DSL-JSON 比 Jackson 快很多,它的优化代码在这里


https://github.com/ngs-doo/dsl-json/blob/master/library/src/main/java/com/dslplatform/json/NumberConverter.java

0?wx_fmt=png

点击图片可以放大浏览


整数是直接从输入的字节里计算出来的,公式是 value = (value << 3) + (value << 1) + ind; 相比读出字符串,然后调用 Integer.valueOf ,这个实现只遍历了一遍输入,同时也避免了内存分配。

Jsoniter 在这个基础上做了循环展开

0?wx_fmt=png

点击图片可以放大浏览


Encode Integer


编码方面情况如何呢?和编码一样的测试数据,测试结果如下:


相比 Jackson ns/op
Protobuf 2.9 121027.285
Thrift 0.17 2128221.323
Jsoniter 2.02 173912.732
DSL-JSON 2.18 161038.645
Fastjson 0.81 431348.853
Jackson 1 351430.048
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值