cereal 和 Boost Serialization 是否使用零拷贝?
Do cereal and Boost Serialization use zero-copy?
我对几个序列化协议做了一些性能比较,包括 FlatBuffers、Cap'n Proto、Boost 序列化和 cereal。所有测试都是用 C++ 编写的。
我知道 FlatBuffers 和 Cap'n Proto 使用零拷贝。使用零拷贝,序列化时间为空,但序列化对象的大小更大。
我以为cereal 和Boost 序列化没有使用零拷贝。但是,序列化时间(对于 int 和 double)几乎为零,并且序列化对象的大小几乎与 Cap'n Proto 或 Flatbuffers 的相同。我在他们的文档中没有找到任何关于零拷贝的信息。
谷物和 Boost 序列化是否也使用零拷贝?
Note: I bountied the other answer which understood the full scope of the question better
Boost 序列化是可扩展的。
它允许您的类型描述需要序列化的内容,并允许档案描述格式。
这可以是 "zero-copy" - 即唯一的缓冲是在接收数据的流中(例如套接字或文件描述符)。
有关有意识地 zero-copy 实现 dynamic_bitset 序列化的示例,请参阅此答案中的代码:
我在网站上有很多。另请查看 BOOST_IS_BITWISE_SERIALIZABLE
的文档及其对容器序列化的影响(如果序列化连续分配的 bitwise-serializable 数据集合,结果是 zero-copy 甚至 __memcpy_sse4
等)。
Side-note: Cap'n proto does something else entirely, AFAIK: it marshals some objects as futures-to-the-data. This is apparently what they advertise aggressively as "∞% faster, 0µs!!!" (which is somewhat true in the case where the data is never retrieved).
Boost 和 Cereal 不 在 Cap'n Proto 或 Flatbuffers 的意义上实现 zero-copy。
使用真正的 zero-copy 序列化,实时 in-memory 对象的后备存储实际上与传递给 read()
或 write()
的内存段完全相同系统调用。根本没有 packing/unpacking 步骤。
一般来说,这有很多含义:
- 未使用 new/delete 分配对象。构造消息时,首先分配消息,它为消息内容分配了一个长的连续内存space。然后,您直接在消息 中分配消息结构,接收实际上指向消息内存的指针。稍后写入消息时,单个
write()
调用将整个内存 space 推到线路上。
- 类似地,当您读入一条消息时,单个
read()
调用(或可能 2-3 次)会将整条消息读入一个内存块。然后,您将获得一个指向消息 "root" 的指针(或 pointer-like 对象),您可以使用它来遍历它。请注意,在您的应用程序遍历之前,不会实际检查消息的任何部分。
- 对于普通套接字,数据的唯一副本发生在 内核 space 中。使用 RDMA 网络,您甚至可以避免 kernel-space 副本:数据从线路直接进入其最终内存位置。
- 当处理文件(而不是网络)时,可以直接从磁盘
mmap()
非常大的消息并直接使用映射的内存区域。这样做是 O(1)——文件有多大并不重要。当您实际访问文件的必要部分时,您的操作系统将自动分页。
- 同一台机器上的两个进程可以通过没有副本的共享内存段进行通信。请注意,通常,常规的旧 C++ 对象在共享内存中不能很好地工作,因为内存段在两个内存 space 中通常没有相同的地址,因此所有指针都是错误的。使用 zero-copy 序列化框架,指针通常表示为偏移量而不是绝对地址,因此它们是 position-independent.
Boost 和 Cereal 不同:当您在这些系统中收到消息时,首先会对整个消息执行传递以 "unpack" 内容。数据的最终存放位置是使用 new/delete 以传统方式分配的对象。类似地,在发送消息时,必须从这棵对象树中收集数据并将其打包到一个缓冲区中以便写出。尽管 Boost 和 Cereal 是 "extensible",但真正 zero-copy 需要非常不同的底层设计;它不能 bolted-in 作为扩展名。
就是说,不要假设 zero-copy 总是会更快。 memcpy()
可以非常快,而您的程序的其余部分可能会使成本相形见绌。同时,zero-copy 系统往往有不方便的 API,特别是因为对内存分配的限制。总的来说,使用传统的序列化系统可能会更好地利用您的时间。
zero-copy 最明显的优势是在操作文件时,因为正如我提到的,您可以轻松地 mmap()
一个大文件并且只读取其中的一部分。非 zero-copy 格式根本无法做到这一点。但是,当谈到网络时,优势就不那么明显了,因为网络通信本身必然是 O(n)。
归根结底,如果您真的想知道哪种序列化系统对您的用例来说最快,您可能需要全部尝试并进行测量。请注意,玩具基准通常具有误导性;您需要测试您的实际用例(或非常相似的东西)以获得有用的信息。
披露:我是 Cap'n Proto(一个 zero-copy 序列化器)和 Protocol Buffers v2(一个流行的非 zero-copy 序列化器)的作者。
我对几个序列化协议做了一些性能比较,包括 FlatBuffers、Cap'n Proto、Boost 序列化和 cereal。所有测试都是用 C++ 编写的。
我知道 FlatBuffers 和 Cap'n Proto 使用零拷贝。使用零拷贝,序列化时间为空,但序列化对象的大小更大。
我以为cereal 和Boost 序列化没有使用零拷贝。但是,序列化时间(对于 int 和 double)几乎为零,并且序列化对象的大小几乎与 Cap'n Proto 或 Flatbuffers 的相同。我在他们的文档中没有找到任何关于零拷贝的信息。
谷物和 Boost 序列化是否也使用零拷贝?
Note: I bountied the other answer which understood the full scope of the question better
Boost 序列化是可扩展的。
它允许您的类型描述需要序列化的内容,并允许档案描述格式。
这可以是 "zero-copy" - 即唯一的缓冲是在接收数据的流中(例如套接字或文件描述符)。
有关有意识地 zero-copy 实现 dynamic_bitset 序列化的示例,请参阅此答案中的代码:
我在网站上有很多。另请查看 BOOST_IS_BITWISE_SERIALIZABLE
的文档及其对容器序列化的影响(如果序列化连续分配的 bitwise-serializable 数据集合,结果是 zero-copy 甚至 __memcpy_sse4
等)。
Side-note: Cap'n proto does something else entirely, AFAIK: it marshals some objects as futures-to-the-data. This is apparently what they advertise aggressively as "∞% faster, 0µs!!!" (which is somewhat true in the case where the data is never retrieved).
Boost 和 Cereal 不 在 Cap'n Proto 或 Flatbuffers 的意义上实现 zero-copy。
使用真正的 zero-copy 序列化,实时 in-memory 对象的后备存储实际上与传递给 read()
或 write()
的内存段完全相同系统调用。根本没有 packing/unpacking 步骤。
一般来说,这有很多含义:
- 未使用 new/delete 分配对象。构造消息时,首先分配消息,它为消息内容分配了一个长的连续内存space。然后,您直接在消息 中分配消息结构,接收实际上指向消息内存的指针。稍后写入消息时,单个
write()
调用将整个内存 space 推到线路上。 - 类似地,当您读入一条消息时,单个
read()
调用(或可能 2-3 次)会将整条消息读入一个内存块。然后,您将获得一个指向消息 "root" 的指针(或 pointer-like 对象),您可以使用它来遍历它。请注意,在您的应用程序遍历之前,不会实际检查消息的任何部分。 - 对于普通套接字,数据的唯一副本发生在 内核 space 中。使用 RDMA 网络,您甚至可以避免 kernel-space 副本:数据从线路直接进入其最终内存位置。
- 当处理文件(而不是网络)时,可以直接从磁盘
mmap()
非常大的消息并直接使用映射的内存区域。这样做是 O(1)——文件有多大并不重要。当您实际访问文件的必要部分时,您的操作系统将自动分页。 - 同一台机器上的两个进程可以通过没有副本的共享内存段进行通信。请注意,通常,常规的旧 C++ 对象在共享内存中不能很好地工作,因为内存段在两个内存 space 中通常没有相同的地址,因此所有指针都是错误的。使用 zero-copy 序列化框架,指针通常表示为偏移量而不是绝对地址,因此它们是 position-independent.
Boost 和 Cereal 不同:当您在这些系统中收到消息时,首先会对整个消息执行传递以 "unpack" 内容。数据的最终存放位置是使用 new/delete 以传统方式分配的对象。类似地,在发送消息时,必须从这棵对象树中收集数据并将其打包到一个缓冲区中以便写出。尽管 Boost 和 Cereal 是 "extensible",但真正 zero-copy 需要非常不同的底层设计;它不能 bolted-in 作为扩展名。
就是说,不要假设 zero-copy 总是会更快。 memcpy()
可以非常快,而您的程序的其余部分可能会使成本相形见绌。同时,zero-copy 系统往往有不方便的 API,特别是因为对内存分配的限制。总的来说,使用传统的序列化系统可能会更好地利用您的时间。
zero-copy 最明显的优势是在操作文件时,因为正如我提到的,您可以轻松地 mmap()
一个大文件并且只读取其中的一部分。非 zero-copy 格式根本无法做到这一点。但是,当谈到网络时,优势就不那么明显了,因为网络通信本身必然是 O(n)。
归根结底,如果您真的想知道哪种序列化系统对您的用例来说最快,您可能需要全部尝试并进行测量。请注意,玩具基准通常具有误导性;您需要测试您的实际用例(或非常相似的东西)以获得有用的信息。
披露:我是 Cap'n Proto(一个 zero-copy 序列化器)和 Protocol Buffers v2(一个流行的非 zero-copy 序列化器)的作者。