协议缓冲区如何比 XML 和 JSON 快?

Posted

技术标签:

【中文标题】协议缓冲区如何比 XML 和 JSON 快?【英文标题】:How are protocol-buffers faster than XML and JSON? 【发布时间】:2019-02-08 07:58:05 【问题描述】:

我最近开始在工作中阅读和使用 gRPC。 gRPC 在内部使用协议缓冲区作为其 IDL,我一直在读到,与 JSON 和 XML 相比,协议缓冲区的性能更好、更快。

我不明白的是——他们是怎么做到的?与 XML 和 JSON 相比,protocol-buffers 中的哪些设计实际上使它们的执行速度更快?

【问题讨论】:

二进制格式。少浪费。以不可读为代价。 协议缓冲区使用优化的二进制格式。此外,定义消息中内容的元信息不包括在消息中。例如。如果您的消息有一个名为foo 的属性,则此名称不是消息的一部分。在 XML 和 JSON 中,您将在消息中包含 foo 作为属性 foo 每次出现的文字字符串。结果是,与 XML 或 JSON 中的相同消息相比,协议缓冲区消息非常紧凑。 他们的文档中有一个非常好的解释:developers.google.com/protocol-buffers/docs/overview Chapter 'Why not xml' 大声笑,我不敢相信我从两年前就发现了这个问题,只是注意到一个小时前进行了编辑(我到达这里后不久) 【参考方案1】:

数据的字符串表示:

需要文本编码/解码(这可能很便宜,但仍然是一个额外的步骤) 需要复杂的解析代码,特别是如果存在“必须允许空格”等人性化规则时 通常涉及更多带宽 - 因此需要更多实际有效负载 - 由于嵌入名称等内容,并且(再次)必须处理对人类友好的表示(例如,如何标记语法) 通常需要大量用于成员查找等的中间字符串实例

基于文本和基于二进制的序列化程序都可以快速高效(或缓慢而可怕)......只是:二进制序列化程序具有倾斜的优势。这意味着“好的”二进制序列化程序通常比“好的”基于文本的序列化程序更快。

让我们比较一个整数的基本例子:

json:

"id":42

如果我们假设 ASCII 或 UTF-8 编码且没有空格,则为 9 个字节。

xml:

<id>42</id>

如果我们假设 ASCII 或 UTF-8 编码并且没有空格 - 并且没有像命名空间这样的命名空间噪音,则为 11 个字节。

protobuf:

0x08 0x2a

2 个字节

现在想象一下编写一个通用的 xml 或 json 解析器,以及您需要处理的所有歧义和场景就在文本层,那么您需要将文本标记 "id" 映射到一个成员,那么您需要对"42" 进行整数解析。在 protobuf 中,payload 更小,加上数学很简单,并且 member-lookup 是一个整数(所以:适合非常快的switch/jump)。

【讨论】:

顺便说一句:如果有人不确定0x08 0x2a 是否代表相同的场景:protogen.marcgravell.com/decode?hex=082a 顺便说一句:编码参考developers.google.com/protocol-buffers/docs/encoding【参考方案2】:

虽然二进制协议在理论上具有优势,但在实践中,它们的性能可能会输给 JSON 或其他具有文本表示的协议,具体取决于实现。

RapidJSON 或 jsoniter-scala 等高效 JSON 解析器以每字节 2-8 个周期的速度解析大多数 JSON 样本。它们的序列化效率更高,除了一些边缘情况,如浮点数,当序列化速度下降到每字节 16-32 个周期时。

但是对于大多数没有大量浮点数或双倍数的域来说,它们的速度与最好的二进制序列化程序相当。请查看 jsoniter-scala 解析和序列化与 ProtoBuf 的 Java 和 Scala 库相当的基准测试结果:

https://github.com/dkomanov/scala-serialization/pull/8

【讨论】:

【参考方案3】:

我不得不争辩说,二进制协议通常总是会在性能方面胜过基于文本的协议。哈,你不会发现很多(或任何)视频流应用程序使用 JSON 来表示帧数据。但是,任何设计不佳的数据结构在解析时都会遇到困难。我参与过许多通信项目,将基于文本的协议替换为“二进制协议”。

【讨论】:

相关,但不回答问题。

以上是关于协议缓冲区如何比 XML 和 JSON 快?的主要内容,如果未能解决你的问题,请参考以下文章

协议缓冲区比序列化更好?

strstr 比算法快?

缓存一致性协议

.Net 协议缓冲区到 JSON,JsonFormatReader 类不处理最外面的花括号?

使用协议缓冲区的 Android Http 请求和响应

带有协议缓冲区的 ASP.NET WebApi - 错误处理