序列化可变状态并通过网络异步发送它,几乎是零拷贝(Cap'n Proto + ZeroMQ)
Serializing mutable state and sending it asynchronously over the network with nearly-zero-copy (Cap'n Proto + ZeroMQ)
我有一个应用程序,我想在其中通过网络将其可变状态的一部分发送到另一台机器(将有这些机器的集群)以进行一些 CPU 密集型计算它并取回结果。就像异步 RPC。这样的调用在程序执行过程中会发生很多次,所以我想尽可能地减少开销,例如最小化数据冗余副本的数量。数据的大小从几十字节到几百KB不等,甚至可能只有几MB。它的结构比较复杂,由一组对象树组成,但是叶子只包含原始类型,内部节点包含最少的元数据。
我正在考虑使用 Cap'n Proto 进行序列化(不过,在这种情况下,我必须为我的数据创建一个冗余模型),以及使用 ZeroMQ 进行传输。在 client/main 应用程序方面,我想使用 azmq,因为我需要 Boost:Asio 的功能(即 coroutine/fiber 支持)。语言是C++。
粗略总结一下:
RelativelyComplexState data;
CapnProtoRequest cp_req = buildRequest(data); // traverses my data, creates C'n P object
azmq_socket.async_send(boost::asio::buffer(cp_req, cp_req.size)); //azmq always copies the buffer? Not good.
// do other stuff while request is being processed remotely
// get notification from azmq/Boost:Asio when reply has arrived
azmq::message msg();
azmq_socket.async_receive(some_message_handler?); // get all the data into msg
CapnProtoResponse cp_resp = parseResponse(msg.cbuffer()); // interpret bytes as C'n P object, hopefully no copy
RelativelySimpleResult result = deserialize(cp_resp);
这样可行吗,或者有更好的方法吗?无模式序列化方法(即 Boost::Serialization)是否会让我的生活更轻松 and/or 在这种情况下应用程序更高效?
此外,使用 ZeroMQ/azmq 发送和接收 Cap'n Proto 对象的最佳方式是什么,避免不必要的复制?通过查看 azmq 的源代码,似乎对于发送,azmq 总是复制缓冲区内容。更微妙的问题是什么(segmenting/framing,等等)?我不熟悉这些库,也没有找到任何解释或好的例子。
谢谢!
我不太了解 ZeroMQ 的接口,但我可以就如何最小化 Cap'n Proto 的副本提供建议。
在发送方,使用capnp::MessageBuilder::getSegmentsForOutput()
(capnp/message.h
) 获取直接指向邮件内容的指针,无需复制。这为您提供了一组字节数组(实际上是单词,但您可以将它们转换为字节)。您需要以某种方式将它们提供给 ZeroMQ 而无需复制它们。您需要确保保留段之间的边界——目标是在接收端提出完全相同的数组数组。也许 ZeroMQ 明确支持多段消息并且可以为您记住段边界;如果没有,您需要在消息前加上 table 段大小前缀。
在接收方,一旦重建了段数组,构造一个 capnp::SegmentArrayMessageReader
(capnp/message.h
) 并将数组传递给构造函数。这将在不复制的情况下使用基础数据。 (请注意,您需要确保数据在 64 位边界上对齐。我不确定 ZeroMQ 是否保证这一点。)
请注意,如果您的客户端和服务器都是 C++,您可能需要考虑使用 Cap'n Proto 自己的 RPC 协议,它更容易设置并且已经避免了所有不必要的副本。然而,将 Cap'n Proto 的事件循环与 boost::asio
集成目前并非易事。这是可能的——例如,您可以查看 node-capnp,它将 Cap'n Proto 与 libuv 的事件循环集成在一起——但可能比您想要做的工作更多。
(披露:我是 Cap'n Proto 的作者。)
我有一个应用程序,我想在其中通过网络将其可变状态的一部分发送到另一台机器(将有这些机器的集群)以进行一些 CPU 密集型计算它并取回结果。就像异步 RPC。这样的调用在程序执行过程中会发生很多次,所以我想尽可能地减少开销,例如最小化数据冗余副本的数量。数据的大小从几十字节到几百KB不等,甚至可能只有几MB。它的结构比较复杂,由一组对象树组成,但是叶子只包含原始类型,内部节点包含最少的元数据。
我正在考虑使用 Cap'n Proto 进行序列化(不过,在这种情况下,我必须为我的数据创建一个冗余模型),以及使用 ZeroMQ 进行传输。在 client/main 应用程序方面,我想使用 azmq,因为我需要 Boost:Asio 的功能(即 coroutine/fiber 支持)。语言是C++。
粗略总结一下:
RelativelyComplexState data;
CapnProtoRequest cp_req = buildRequest(data); // traverses my data, creates C'n P object
azmq_socket.async_send(boost::asio::buffer(cp_req, cp_req.size)); //azmq always copies the buffer? Not good.
// do other stuff while request is being processed remotely
// get notification from azmq/Boost:Asio when reply has arrived
azmq::message msg();
azmq_socket.async_receive(some_message_handler?); // get all the data into msg
CapnProtoResponse cp_resp = parseResponse(msg.cbuffer()); // interpret bytes as C'n P object, hopefully no copy
RelativelySimpleResult result = deserialize(cp_resp);
这样可行吗,或者有更好的方法吗?无模式序列化方法(即 Boost::Serialization)是否会让我的生活更轻松 and/or 在这种情况下应用程序更高效?
此外,使用 ZeroMQ/azmq 发送和接收 Cap'n Proto 对象的最佳方式是什么,避免不必要的复制?通过查看 azmq 的源代码,似乎对于发送,azmq 总是复制缓冲区内容。更微妙的问题是什么(segmenting/framing,等等)?我不熟悉这些库,也没有找到任何解释或好的例子。
谢谢!
我不太了解 ZeroMQ 的接口,但我可以就如何最小化 Cap'n Proto 的副本提供建议。
在发送方,使用capnp::MessageBuilder::getSegmentsForOutput()
(capnp/message.h
) 获取直接指向邮件内容的指针,无需复制。这为您提供了一组字节数组(实际上是单词,但您可以将它们转换为字节)。您需要以某种方式将它们提供给 ZeroMQ 而无需复制它们。您需要确保保留段之间的边界——目标是在接收端提出完全相同的数组数组。也许 ZeroMQ 明确支持多段消息并且可以为您记住段边界;如果没有,您需要在消息前加上 table 段大小前缀。
在接收方,一旦重建了段数组,构造一个 capnp::SegmentArrayMessageReader
(capnp/message.h
) 并将数组传递给构造函数。这将在不复制的情况下使用基础数据。 (请注意,您需要确保数据在 64 位边界上对齐。我不确定 ZeroMQ 是否保证这一点。)
请注意,如果您的客户端和服务器都是 C++,您可能需要考虑使用 Cap'n Proto 自己的 RPC 协议,它更容易设置并且已经避免了所有不必要的副本。然而,将 Cap'n Proto 的事件循环与 boost::asio
集成目前并非易事。这是可能的——例如,您可以查看 node-capnp,它将 Cap'n Proto 与 libuv 的事件循环集成在一起——但可能比您想要做的工作更多。
(披露:我是 Cap'n Proto 的作者。)