我在几种序列化协议(包括FlatBuffers、Cap、Boost序列化和谷物)之间做了一些性能比较。所有的测试都是用C++编写的。
我知道FlatBuffers和船长使用零拷贝。对于零拷贝,序列化时间为空,但序列化对象的大小更大.
我以为麦片和升级版没有使用零拷贝。但是,序列化时间(对于int和double)几乎为空,序列化对象的大小与Cap或Flatbuffers大小几乎相同。我在他们的文件里没有找到任何关于零拷贝的信息。
谷物和促进序列化也使用零拷贝吗?
发布于 2017-01-23 23:24:09
Boost and Cereal do not实现零拷贝,意义上是船长、Proto或Flatbuffers。
对于真正的零拷贝序列化,内存对象的备份存储实际上与传递给read()或write()系统调用的内存段完全相同。根本没有包装/拆包步骤。
一般而言,这有若干影响:
write()调用就会将整个内存空间挤出线路。read()调用(或者可能是2-3)将整个消息读入一个内存块。然后,您将得到一个指向消息“根”的指针(或类似指针的对象),您可以使用该指针遍历消息。注意,在应用程序遍历消息之前,不会实际检查消息的任何部分。mmap()一条非常大的消息,并直接使用映射的内存区域。这样做是O(1) --不管文件有多大。当您实际访问该文件时,您的操作系统将自动在文件的必要部分中页面。Boost和Cereal是不同的:当您在这些系统中接收到一条消息时,首先对整个消息执行一次传递,以“解压缩”内容。数据的最后休息位置是使用new/delete以传统方式分配的对象。类似地,在发送消息时,必须从这个对象树中收集数据,并将其打包到一个缓冲区中,以便写入。尽管Boost和Cereal是“可扩展的”,但真正的零拷贝需要一个非常不同的底层设计;它不能作为扩展被栓入。
尽管如此,不要认为零拷贝总是会更快。memcpy()可以非常快,您的程序的其余部分可能会使成本相形见绌。同时,零拷贝系统往往有不方便的API,特别是由于内存分配的限制。总的来说,更好地利用您的时间使用传统的序列化系统。
零拷贝最明显的优势是在操作文件时,因为正如我所提到的,您可以轻松地mmap()一个巨大的文件,并且只读取其中的一部分。非零拷贝格式根本无法做到这一点。然而,当涉及到网络时,优势就不那么明显了,因为网络通信本身必然是O(n)。
最后,如果您真的想知道哪个序列化系统对于您的用例来说是最快的,那么您可能需要全部尝试并测量它们。请注意,玩具基准通常具有误导性;您需要测试实际用例(或类似的东西)才能获得有用的信息。
公开:我是Cap(一个零拷贝序列化程序)和协议缓冲区v2 (一个流行的非零拷贝序列化程序)的作者。
发布于 2017-01-23 11:09:41
注:我给出了另一个答案,它能更好地理解问题的全部含义。
Boost序列化是可扩展的。
它允许您的类型描述需要序列化的内容,以及描述格式的归档。
这可以是“零拷贝”--也就是说,唯一的缓冲是在接收数据的流中(例如套接字或文件描述符)。
有关dynamic_bitset序列化的有意识的零拷贝实现的示例,请参阅答案:bitset?中的代码。
我在网站上有很多这样的东西。还请看一下BOOST_IS_BITWISE_SERIALIZABLE的文档及其对容器序列化的影响(如果序列化一个连续分配的位序列化数据集合,其结果是零拷贝,甚至是__memcpy_sse4等等)。
附带注意: proto船长完全是在做其他的事情,AFAIK:它将一些对象作为未来的数据。这显然是他们大肆宣传的"∞%快,0!“(在从未检索数据的情况下,这在某种程度上是正确的)。
https://stackoverflow.com/questions/41801826
复制相似问题