19

我对几种序列化协议进行了一些性能比较,包括 FlatBuffers、Cap'n Proto、Boost 序列化和谷物。所有的测试都是用 C++ 编写的。

我知道 FlatBuffers 和 Cap'n Proto 使用零拷贝。使用零拷贝,序列化时间为空,但序列化对象的大小更大。

我认为谷物和 Boost 序列化没有使用零拷贝。但是,序列化时间(对于 int 和 double)几乎为零,并且序列化对象的大小几乎与 Cap'n Proto 或 Flatbuffers 相同。我在他们的文档中没有找到任何关于零拷贝的信息。

谷物和 Boost 序列化是否也使用零拷贝?

4

2 回答 2

28

Boost 和 Cereal没有实现 Cap'n Proto 或 Flatbuffers 意义上的零拷贝。

read()使用真正的零拷贝序列化,实时内存对象的后备存储实际上与传递给orwrite()系统调用的内存段完全相同。根本没有打包/拆包步骤。

一般来说,这有很多含义:

  • 不使用 new/delete 分配对象。构造消息时,首先分配消息,它为消息内容分配一个长的连续内存空间。然后,您直接在 message 内部分配消息结构,接收实际上指向消息内存的指针。稍后写入消息时,一次write()调用会将整个内存空间推到网络上。
  • 同样,当您读入一条消息时,一次read()调用(或者可能是 2-3 次)会将整条消息读入一个内存块。然后,您将获得一个指向消息“根”的指针(或类似指针的对象),您可以使用它来遍历它。请注意,在您的应用程序遍历它之前,实际上不会检查消息的任何部分。
  • 使用普通套接字,数据的唯一副本发生在内核空间中。使用 RDMA 网络,您甚至可以避免内核空间复制:数据从线路直接进入其最终内存位置。
  • 在处理文件(而不是网络)时,可以mmap()直接从磁盘获取非常大的消息并直接使用映射的内存区域。这样做是 O(1) - 文件有多大并不重要。当您实际访问文件的必要部分时,您的操作系统将自动分页。
  • 同一台机器上的两个进程可以通过没有副本的共享内存段进行通信。请注意,一般来说,常规的旧 C++ 对象在共享内存中不能很好地工作,因为内存段在两个内存空间中通常没有相同的地址,因此所有指针都是错误的。对于零拷贝序列化框架,指针通常表示为偏移量而不是绝对地址,因此它们与位置无关。

Boost 和 Cereal 不同:当您在这些系统中收到一条消息时,首先会对整个消息执行一次传递以“解包”内容。数据的最终存放位置是使用 new/delete 以传统方式分配的对象中。类似地,当发送消息时,必须从这棵对象树中收集数据并将其打包到一个缓冲区中才能被写出。尽管 Boost 和 Cereal 是“可扩展的”,但真正实现零拷贝需要非常不同的底层设计;它不能作为扩展用螺栓固定。

也就是说,不要假设零拷贝总是更快。memcpy()可以相当快,而您的程序的其余部分可能会使成本相形见绌。同时,零拷贝系统往往有不方便的 API,特别是因为内存分配的限制。总体而言,使用传统的序列化系统可能会更好地利用您的时间。

零拷贝最明显有利的地方是在处理文件时,因为正如我所提到的,您可以轻松地mmap()获得一个巨大的文件并且只读取其中的一部分。非零拷贝格式根本无法做到这一点。但是,在网络方面,优势不太明显,因为网络通信本身必然是 O(n)。

归根结底,如果您真的想知道哪种序列化系统最适合您的用例,您可能需要全部尝试并测量它们。请注意,玩具基准通常具有误导性;您需要测试您的实际用例(或非常类似的东西)以获得有用的信息。

披露:我是 Cap'n Proto(零拷贝序列化程序)和 Protocol Buffers v2(流行的非零拷贝序列化程序)的作者。

于 2017-01-23T23:24:09.253 回答
2

注意:我增加了另一个答案,它更好地理解了问题的全部范围

Boost 序列化是可扩展的。

它允许您的类型描述需要序列化的内容,以及描述格式的档案。

这可以是“零拷贝”——即唯一的缓冲是在接收数据的流中(例如套接字或文件描述符)。

有关 dynamic_bitset 序列化的有意识零拷贝实现的示例,请参见此答案中的代码:如何序列化 boost::dynamic_bitset?

我在网站上有很多这样的。另请查看文档BOOST_IS_BITWISE_SERIALIZABLE及其对容器序列化的影响(如果您序列化连续分配的按位可序列化数据集合,则结果是零拷贝甚至__memcpy_sse4等)。

旁注:Cap'n proto 完全做其他事情,AFAIK:它将一些对象编组为数据的未来。这显然是他们积极宣传的“快∞%,0µs!!!” (这在从未检索到数据的情况下是正确的)。

于 2017-01-23T11:09:41.793 回答