我有一个应用程序,我希望通过网络将其部分可变状态发送到另一台机器(将有一组这些机器)对其进行一些CPU密集型计算并返回结果。像异步RPC一样。这样的调用将在程序执行期间多次发生,因此我希望将开销尽可能小,例如,最小化数据的冗余副本数量。数据大小从几十字节到几百KB不等,甚至可能只有几MB。它的结构相对复杂,它由一组对象树组成,但是叶子只包含原始类型,内部节点包含最少的元数据。
我正在考虑使用Cap& Proto进行序列化(但在这种情况下,我必须为我的数据创建一个冗余模型),以及ZeroMQ用于传输。在客户端/主要应用程序端,我想使用azmq,因为我需要Boost:Asio的功能(即协程/光纤支持)。语言是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)是否会使我的生活更轻松和/或应用程序更高效?
另外,使用ZeroMQ / azmq发送和接收Cap&#39 Proto对象的最佳方法是什么,避免不必要的副本?通过查看azmq的源代码,似乎对于发送,azmq总是复制缓冲区内容。有哪些更微妙的问题(细分/框架等)?我对图书馆并不熟悉,也没有找到任何解释或好的例子。
谢谢!
答案 0 :(得分:7)
我对ZeroMQ的界面了解不多,但我可以提供有关如何最大限度地减少Cap'n Proto副本的建议。
在发送方,使用capnp::MessageBuilder::getSegmentsForOutput()
(capnp/message.h
)直接指向邮件的内容而不进行复制。这为您提供了一个字节数组(实际上是单词,但您可以将它们转换为字节)。你需要以某种方式将这些提供给ZeroMQ而不复制它们。您需要确保保留段之间的边界 - 目标是在接收端提供完全相同的阵列数组。也许ZeroMQ明确支持多段消息,并且可以为您记住段边界;如果没有,您需要在邮件前添加一个分段大小表。
在接收方,重建段数组后,构造一个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的作者。)