【问题标题】:C++ Protocol Buffer, sending integer arrayC++ 协议缓冲区,发送整数数组
【发布时间】:2015-01-07 20:56:11
【问题描述】:

我有一个嵌入式 C++ 项目,我从硬件设备读取一系列 int32,然后将它们打包到一个 int 数组中作为大型数据结构的一部分,然后通过 TCP/IP 发送到远程系统。所以,我使用了一个定义了一堆东西的简单数据结构,现在我想将它转换为使用协议缓冲区。 所以,我正在考虑使用“重复的 int32 数据”作为我的 proto buff 的元素。但我想避免使用这样的循环:

int hardware_data[1000]; // An array that holds the data read from the hardware
for(int i=0; i< sizeof(hardware_data); i++ )
{
    proto.add_data( hardware_data[i] );
}

我更愿意使用一种有效的方法,例如使 proto buff 只指向现有的 hardware_data[] 数组(零复制方法),或者使用 memcpy 从 hardware_data 到 proto.data。

我了解如何设置 memcpy(),但是 proto 爱好者如何知道 proto.data“数组”中有多少元素? 我仍然可以使用 proto.data_size() 来获取元素的数量吗? 有没有一种有效的方法可以将数据从我的硬件读取移动到 proto buff 以进行发送? 有没有更好的方法来做到这一点?

克里克, 我不知道零拷贝 API。这是我的原型定义:

message hardware_data 
{
optional    Lob                     lob             = 1;
optional    int32                   taskSeqNum      = 2;
optional    int32                   secondsOfDay    = 3;
optional    float                   IQOutRateKhz    = 4;
optional    float                   IQBwKhz         = 5;
optional    int32                   tStart          = 6;
optional    int32                   tOffset         = 7;
optional    float                   collectionTime  = 8;
optional    int32                   numSamples      = 9;
optional    int32                   chunk           = 10;
optional    int32                   dimSize         = 11;
repeated    int32                   data            = 12 [packed=true];
}

我不确定零副本将如何影响这个原型 buff 定义。

【问题讨论】:

  • zero copy API 的哪一部分不适合你?
  • sizeof 运算符以 bytes 为单位返回数据结构的大小,因此对于 ints 的 1000 项数组,它可能是 4000,您的循环将索引数组远远超出其实际结束,从而导致未定义的行为。使用sizeof(hadware_data)/sizeof(hardware_data[0]) 计算数组的项数
  • 当然......我只是把它作为示例代码。您的评论虽然正确,但无法回答如何使用协议缓冲区的问题。
  • @KerrekSB 这是一个低级 API,与 rbwilliams 想要的无关。
  • @KentonVarda:啊,对,因为 OP 想要 varint 编码。如果序列化速度是个问题,那么也许固定宽度的整数值得考虑。

标签: c++ protocol-buffers


【解决方案1】:

在网络上,一个打包的重复 int32 被编码为一系列 varint。 varint 是一种可变宽度编码,其中较小的值占用较少的空间。当然,这不是数据在数组中的表示方式,因此将其嵌入到消息零拷贝中是不可能的。

不过,事实上,您目前正在制作两份副本,您可以消除其中一份。考虑将数据直接粘贴到google::protobuf::RepeatedField&lt;int&gt; 中,而不是直接分配int hardware_data[1000]。然后,您可以巧妙地使用 Swap() 将该数据移动到没有副本的消息中:

RepeatedField<int> hardware_data;
hardware_data.Reserve(expected_size);
get_data_somehow(&hardware_data);

// later
proto.mutable_data()->Swap(&hardware_data);

序列化消息后,您可能希望另外将字段 Swap() 回退,以便您可以重用已保留的内存。 (RepeatedField::Clear() 不会释放底层内存,只是标记它以供重用。)

尽管如此,序列化消息仍然需要复制数据作为编码的一部分。即使您将编码更改为打包重复的 fixed32(实际上在线上编码为 32 位整数),也无法说服库直接使用您的内存。

【讨论】:

  • 感谢 Kenton,看来我没有有效的方法来使用 proto buffs。请记住,这是一个嵌入式平台。我正在实时处理大量无线电频谱数据。所以性能对我来说是一个关键问题。将数据从一个地方过度复制到另一个地方是非常浪费的。我认为这是不使用 proto buffs 的一个很好的理由。
  • @rbwilliams:我同意,C++ protobuf 库可能做了太多分配,无法在嵌入式环境中使用。你可以看看 protobuf-c 看看它是否更好。或者,您也可以通过Cap'n Proto 获得更多运气,这完全是为了避免复制。 Cap'n Proto 目前并不完全允许您将外部数组“附加”到消息中,但添加起来非常容易,如果您需要,我很乐意这样做。 (披露:我是 Protobuf C++ 库和 Cap'n Proto 的作者。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多