提升序列化与谷歌协议缓冲区? [关闭]

Posted

技术标签:

【中文标题】提升序列化与谷歌协议缓冲区? [关闭]【英文标题】:boost serialization vs google protocol buffers? [closed] 【发布时间】:2010-11-06 20:46:10 【问题描述】:

有没有使用这些库经验的人对他们更喜欢哪一个有任何评论?使用中是否存在性能差异或困难?

【问题讨论】:

这接近于 ***.com/questions/321619/c-serialization-performance 的副本,所以它也可能会有所帮助 【参考方案1】:

我从来没有使用 boost 的库实现过任何东西,但我发现 Google protobuff 更经过深思熟虑,而且代码更简洁易读。我建议您查看您想要使用的各种语言,并阅读代码和文档并下定决心。

我使用 protobufs 遇到的一个困难是他们在生成的代码 GetMessage() 中命名了一个非常常用的函数,这当然与 Win32 GetMessage 宏冲突。

我仍然强烈推荐 protobufs。它们非常有用。

【讨论】:

我的限额恢复后会 +1。感谢您提及GetMessage 障碍。需要考虑的事情......【参考方案2】:

我没有使用 boost 序列化的经验,但我使用过协议缓冲区。我非常喜欢协议缓冲区。请记住以下几点(我在 不知道提升的情况下这么说)。

协议缓冲区非常有效,所以我不认为与 boost 相比这是一个严重的问题。 协议缓冲区提供了一种与其他语言(Python 和 Java... 以及更多正在开发中的语言)一起使用的中间表示。如果你知道你只使用 C++,也许 boost 更好,但使用其他语言的选项很好。 协议缓冲区更像是数据容器...没有面向对象的特性,例如继承。考虑一下您想要序列化的内容的结构。 协议缓冲区很灵活,因为您可以添加“可选”字段。这基本上意味着您可以在不破坏兼容性的情况下更改协议缓冲区的结构。

希望这会有所帮助。

【讨论】:

【参考方案3】:

boost.serialization 只需要 C++ 编译器并为您提供一些语法糖,例如

serialize_obj >> archive;
// ...
unserialize_obj << archive;

用于保存和加载。如果 C++ 是您使用的唯一语言,您应该认真对待 boost.serialization。

我快速浏览了 google 协议缓冲区。从我所看到的情况来看,我会说它与 boost.serialization 没有直接的可比性。您必须将 .proto 文件的编译器添加到您的工具链并维护 .proto 文件本身。 API 不像 boost.serialization 那样集成到 C++ 中。

boost.serialization 很好地完成了它设计的工作:序列化 C++ 对象 :) OTOH 像 google 协议缓冲区这样的查询 API 为您提供了更大的灵活性。

由于到目前为止我只使用了 boost.serialization,我无法对性能比较发表评论。

【讨论】:

【参考方案4】:

我在这两个系统上都玩了一点,没什么大不了的,只是一些简单的骇人听闻的东西,但我觉得你应该如何使用这些库有很大的不同。

使用 boost::serialization,您首先编写自己的结构/类,然后添加归档方法,但您仍然留下了一些非常“苗条”的类,它们可以用作数据成员、继承等.

使用协议缓冲区,即使是一个简单的结构,生成的代码量也相当可观,并且生成的结构和代码更多地用于操作,并且您使用协议缓冲区的功能来传输数据到您的自己的内部结构。

【讨论】:

很多时候,您不需要在自己的内部结构之间传输数据 - 您可以直接使用协议缓冲区填充这些结构。它们本质上是值类,具有移动语义,并且可以很好地与 STL 和 Boost 集合配合使用。【参考方案5】:

我已经使用 Boost Serialization 很长时间了,只是深入研究了协议缓冲区,我认为它们的用途并不完全相同。 BS(没看到)将您的 C++ 对象保存到流中,而 PB 是您读取/读取的交换格式。

PB 的数据模型要简单得多:您可以获得各种整数和浮点数、字符串、数组、基本结构,仅此而已。 BS 允许您一步直接保存所有对象。

这意味着使用 BS,您可以在线获取更多数据,但不必重建所有对象结构,而协议缓冲区更紧凑,但在读取存档后还有更多工作要做。顾名思义,一种用于协议(与语言无关,节省空间的数据传递),另一种用于序列化(节省对象)。

那么对您来说更重要的是:速度/空间效率还是简洁的代码?

【讨论】:

【参考方案6】:

提升序列化

是一个用于将数据写入流的库。 不压缩数据。 不支持自动数据版本控制。 支持 STL 容器。 写入数据的属性取决于选择的流(例如字节序、压缩)。

协议缓冲区

根据接口描述生成代码(默认支持 C++、Python 和 Java。C、C# 和其他第三方)。 可选择压缩数据。 自动处理数据版本控制。 处理平台之间的字节序交换。 不支持 STL 容器。

Boost 序列化是一个用于将对象转换为序列化数据流的库。协议缓冲区做同样的事情,但也为您做其他工作(如版本控制和字节序交换)。对于“小型简单任务”,Boost 序列化更简单。协议缓冲区可能更适合“更大的基础设施”。

编辑:24-11-10:将“自动”添加到 BS 版本控制中。

【讨论】:

嗯...这似乎不完全正确,因为 BS 有版本控制,据我所知,字节序问题取决于您使用的序列化格式。 好的,不支持是自动的。您必须在序列化代码中显式构建版本控制,序列化程序不会为您执行此操作。 boost 序列化从一开始就包括数据的版本控制。这是默认的和自动的。如果您不希望对某些数据类型进行版本控制,您可以按类型禁用它。但一般来说,这几乎从来没有做过。 数据压缩一般是通过boost iostream的组合来完成的。它不是 boost 序列化的一部分,因为这样做是多余的。 使用 boost 的数据压缩非常容易添加到组合中:***.com/questions/1753469/…【参考方案7】:

关于Boost序列化的更正(猜猜这是that answer):

它确实允许支持data versioning。

如果您需要压缩 - 使用压缩流。

可以处理平台之间的字节序交换,因为编码可以是文本、二进制或 XML。

【讨论】:

【参考方案8】:

关于 boost.serialization 有几个额外的问题,我将添加到组合中。警告:除了浏览文档之外,我对协议缓冲区没有任何直接经验。

请注意,虽然我认为 boost 和 boost.serialization 的功能非常出色,但我得出的结论是,它附带的默认存档格式不是有线格式的好选择。

区分你的类的版本很重要(正如其他答案中提到的,boost.serialization 对数据版本控制有一些支持)和不同版本的序列化库之间的兼容性 em>。

boost.serialization may not generate archives that older versions can deserialize 的较新版本。(反之则不然:较新版本总是旨在反序列化旧版本制作的档案)。这给我们带来了以下问题:

我们的客户端和服务器软件都创建了另一个使用的序列化对象,因此如果我们同步升级客户端和服务器,我们只能移动到更新的 boost.serialization。 (在您无法完全控制客户的环境中,这是一个相当大的挑战)。 Boost 捆绑为一个带有共享部分的大型库,序列化代码和 boost 库的其他部分(例如 shared_ptr)可能在同一个文件中使用,我无法升级 任何 boost 的一部分,因为我无法升级 boost.serialization。我不确定尝试将多个版本的 boost 链接到一个可执行文件中是否可能/安全/理智,或者我们是否有预算/精力将需要保留在旧版本 boost 上的位重构为单独的可执行文件(在我们的例子中是 DLL)。 我们使用的旧版本 boost 不支持我们使用的最新版本的编译器,所以我们也使用旧版本的编译器。

Google 似乎实际上是 publish the protocol buffers wire format,而 Wikipedia 将它们描述为 forwards-compatible, backwards-compatible(尽管我认为 Wikipedia 指的是数据版本控制而不是协议缓冲区库版本控制)。虽然这些都不能保证前向兼容性,但对我来说这似乎是一个更强有力的指示。

总之,当我无法同步升级客户端和服务器时,我更喜欢众所周知的、已发布的有线格式,例如协议缓冲区。

脚注:我的 related answer 的无耻插件。

【讨论】:

仅供参考:2.5 年过去了,我现在在协议缓冲区方面拥有丰富的直接经验,我支持我上面的言论。 “我们使用的旧版本的 boost 不支持我们使用的最新版本的编译器”是什么意思? Boost 是否使用了一些非标准的语言扩展,或者为什么会这样? 好问题。我认为是的——例如,我猜 boost.thread 和 boost.interprocess 使用(或使用)特定于平台的 API 来实现可能无法以可移植方式实现的特性。此外,编译器的标准一致性因版本而异,因此适用于例如MSVC 2005 可能无法与更高版本一起工作/编译。 Boost.org 记录了哪些编译器可以工作:boost.org/doc/libs/1_67_0/libs/log/doc/html/log/…【参考方案9】:

与工程领域的几乎所有事情一样,我的回答是……“这取决于。”

两者都是经过充分测试和审查的技术。两者都会获取您的数据并将其转换为适合发送到某个地方的数据。两者都可能足够快,如果你真的在这里或那里计算一个字节,你可能不会对任何一个感到满意(让我们面对它创建的数据包将是 XML 或 JSON 的一小部分)。

对我来说,这实际上归结为工作流程,以及您是否需要 C++ 以外的其他东西。

如果您想首先弄清楚您的消息内容并且您正在从头开始构建一个系统,请使用协议缓冲区。您可以以抽象的方式考虑消息,然后以您想要的任何语言自动生成代码(几乎所有内容都可以使用 3rd 方插件)。此外,我发现使用 Protocol Buffers 简化了协作。我只需发送一个 .proto 文件,然后其他团队就可以清楚地了解正在传输的数据。我也不会对他们强加任何东西。如果他们想使用 Java,那就去吧!

如果我已经在 C++ 中构建了一个类(并且这种情况经常发生)并且我现在想通过网络发送该数据,那么 Boost 序列化显然很有意义(尤其是在我已经有了 Boost 的情况下)依赖于其他地方)。

【讨论】:

【参考方案10】:

您可以将 boost 序列化与“真实”域对象紧密结合使用,并序列化完整的对象层次结构(继承)。 Protobuf 不支持继承,所以你必须使用聚合。人们认为 Protobuf 应该用于 DTO(数据传输对象),而不是核心域对象本身。我同时使用了 boost::serialization 和 protobuf。应该考虑 boost::serialization 的性能,cereal 可能是一个替代方案。

【讨论】:

【参考方案11】:

我知道这是一个老问题了,但我想我会把我的 2 便士扔进去!

有了 boost,您就有机会在您的课程中编写一些数据验证;这很好,因为数据定义和有效性检查都在一个地方。

使用 GPB,您能做的最好的事情就是将 cmets 放入 .proto 文件中,并希望所有使用它的人都能阅读它、关注它并自行实施有效性检查。

不用说,如果您依靠网络流另一端的其他人以与自己相同的活力来执行此操作,这不太可能且不可靠。此外,如果对有效性的约束发生变化,则需要计划、协调和完成多个代码更改。

因此,我认为 GPB 不适合很少有机会与所有团队成员定期会面和交谈的开发。

==编辑==

我的意思是这样的:

message Foo

    int32 bearing = 1;

现在谁来说bearing 的有效范围是多少?我们可以有

message Foo

    int32 bearing = 1;  // Valid between 0 and 359

但这取决于其他人阅读并为其编写代码。例如,如果你编辑它并且约束变为:

message Foo

    int32 bearing = 1;  // Valid between -180 and +180

您完全依赖于使用此 .proto 更新其代码的每个人。这是不可靠且昂贵的。

至少使用 Boost 序列化,您正在分发单个 C++ 类,并且可以在其中内置数据有效性检查。如果这些限制条件发生变化,那么除了确保他们使用与您相同版本的源代码外,其他人无需做任何工作。

替代方案

还有一个替代方案:ASN.1。这是古老的,但有一些非常非常方便的东西:

Foo ::= SEQUENCE

   bearing INTEGER (0..359)

注意约束。因此,每当有人使用此 .asn 文件、生成代码时,他们最终都会得到自动检查 bearing 是否介于 0 和 359 之间的代码。如果您更新 .asn 文件,

Foo ::= SEQUENCE

   bearing INTEGER (-180..180)

他们需要做的就是重新编译。无需更改其他代码。

你也可以这样做:

bearingMin INTEGER ::= 0
bearingMax INTEGER ::= 360

Foo ::= SEQUENCE

   bearing INTEGER (bearingMin..<bearingMax)

注意&lt;。而且在大多数工具中,bearingMin 和bearingMax 可以在生成的代码中显示为常量。这非常有用。

约束可能非常复杂:

Garr ::= INTEGER (0..10 | 25..32)

看看这个PDF中的第13章;你能做的真是太棒了;

数组也可以被约束:

Bar ::= SEQUENCE (SIZE(1..5)) OF Foo
Sna ::= SEQUENCE (SIZE(5)) OF Foo
Fee ::= SEQUENCE 

    boo SEQUENCE (SIZE(1..<6)) OF INTEGER (-180<..<180)

ASN.1 是老式的,但仍在积极开发、广泛使用(您的手机经常使用它),并且比大多数其他序列化技术更灵活。我能看到的唯一不足是没有像样的 Python 代码生成器。如果您使用的是 C/C++、C#、Java、ADA,那么免费(C/C++、ADA)和商业(C/C++、C#、JAVA)工具的组合将为您提供很好的服务。

我特别喜欢二进制和基于文本的线格式的广泛选择。这使得它在某些项目中非常方便。有线格式列表目前包括:

BER(二进制) PER(二进制、对齐和未对齐。这是超位效率。例如,限制在015 之间的INTEGER 将只占用线路上的4 bits) OER DER(另一个二进制文件) XML(也是 XER) JSON(全新,工具支持仍在开发中)

加上其他人。

注意最后两个?是的,您可以在 ASN.1 中定义数据结构、生成代码并以 XML 和 JSON 格式发出/使用消息。对于一项始于 1980 年代的技术来说还不错。

版本控制与 GPB 不同。您可以允许扩展:

Foo ::= SEQUENCE

   bearing INTEGER (-180..180),
   ...

这意味着以后我可以添加到Foo,具有此版本的旧系统仍然可以工作(但只能访问bearing 字段)。

我对 ASN.1 的评价很高。处理起来可能很痛苦(工具可能要花钱,生成的代码不一定漂亮,等等)。但是这些限制是一个真正奇妙的功能,它一次又一次地为我节省了大量的心痛。当编码器/解码器报告他们生成了 duff 数据时,开发人员会发牢骚。

其他链接:

Good intro Open source C/C++ compiler Open source compiler, does ADA too AFAIK Commercial, good Commercial, good Try it yourself online

观察

共享数据:

代码优先方法(例如 Boost 序列化)将您限制为使用原始语言(例如 C++),或者迫使您使用另一种语言做大量额外工作 首先使用架构更好,但是 其中很多在共享合同中留下了很大的空白(即没有限制)。 GPB 在这方面很烦人,因为它在其他方面非常好。 有些有限制(例如 XSD、JSON),但受到不完整的工具支持。 例如,Microsoft 的 xsd.exe 主动忽略 xsd 文件中的约束(MS 的借口实在是微不足道)。 XSD 很好(从约束的角度来看),但如果你不能相信其他人会使用一个好的 XSD 工具来为他/她强制执行它们,那么 XSD 的价值就会降低 JSON 验证器没问题,但它们一开始就无法帮助您形成 JSON,并且不会自动调用。无法保证向您发送 JSON 消息的人已经通过验证器运行了它。您必须记得自己验证。 ASN.1 工具似乎都实现了约束检查。

所以对我来说,ASN.1 做到了。它是最不可能导致其他人犯错的那个,因为它具有正确的功能,并且所有工具似乎都在努力完全实现这些功能,并且对于大多数用途而言,它是语言中立的。

说实话,如果 GPB 添加了约束机制,那将是赢家。 XSD 很接近,但这些工具几乎都是垃圾。如果有其他语言的不错的代码生成器,JSON 模式会非常好。

如果 GPB 添加了约束(注意:这不会改变任何有线格式),那将是我向所有人推荐的几乎所有用途的约束。虽然 ASN.1 的 uPER 对于无线电链路非常有用。

【讨论】:

以上是关于提升序列化与谷歌协议缓冲区? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Google 协议缓冲区和 stl 向量、映射和提升共享指针

谷歌协议缓冲区中的未知扩展

如何在谷歌协议缓冲区中根据需要定义重复字段?

无法在 Mac 上构建谷歌协议缓冲区教程

反序列化缓冲区协议缓冲区

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