我在几种序列化协议(protocol)之间做了一些性能比较,包括FlatBuffers,Cap'n Proto,Boost序列化和谷类。所有测试都是用C++编写的。
我知道FlatBuffers和Cap'n Proto使用零复制。对于零复制,序列化时间为空,但序列化对象的大小较大。
我以为 Cereal 和Boost序列化不使用零拷贝。但是,序列化时间(对于int和double)几乎为空,并且序列化对象的大小几乎与Cap'n Proto或Flatbuffers的大小相同。我没有在他们的文档中找到有关零复制的任何信息。
Cereal 和Boost序列化也使用零拷贝吗?
最佳答案
在Cap'n Proto或Flatbuffers的意义上,Boost和Cereal会执行而不是来实现零复制。
通过真正的零副本序列化,活内存中对象的后备存储实际上与传递给read()
或write()
系统调用的内存段完全相同。根本没有包装/拆箱步骤。
通常,这具有许多含义:
write()
调用会将整个内存空间推到线路上。 read()
调用(或者可能是2-3个)将整个消息读入一个内存块中。然后,您将获得一个指向消息“根”的指针(或类似指针的对象),可用于遍历消息。请注意,在您的应用程序遍历该消息之前,不会真正检查该消息的任何部分。 mmap()
,并直接使用映射的内存区域。这样做是O(1)-文件的大小无关紧要。实际访问它们时,操作系统将自动分页文件的必要部分。 Boost和Cereal是不同的:当您在这些系统中收到消息时,首先对整个消息执行传递以“解包”内容。数据的最终存放位置是使用new/delete以传统方式分配的对象。类似地,在发送消息时,必须从该对象树中收集数据并将其打包到一个缓冲区中才能被写出。即使Boost和Cereal是“可扩展的”,但要实现真正的零复制就需要非常不同的底层设计。它不能作为扩展插入。
就是说,不要以为零复制总是会更快。
memcpy()
可能很快,而程序的其余部分可能会使成本相形见.。同时,零拷贝系统倾向于具有不方便的API,特别是由于内存分配的限制。总的来说,使用传统的序列化系统可能会更好地利用您的时间。零复制最明显的优势是在处理文件时,因为正如我提到的那样,您可以轻松地对大文件进行
mmap()
,而只读取其中的一部分。非零拷贝格式根本无法做到这一点。但是,在联网方面,优势并不十分明显,因为网络通信本身必定为O(n)。归根结底,如果您真的想知道哪种序列化系统最适合您的用例,则可能需要全部尝试并进行评估。请注意,玩具基准测试通常会产生误导;您需要测试您的实际用例(或非常类似的用例)以获得有用的信息。
披露:我是Cap'n Proto(零拷贝序列化程序)和Protocol Buffers v2(流行的非零拷贝序列化程序)的作者。
关于c++ - Cereal 和Boost序列化是否使用零拷贝?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41801826/