Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Protocol Buffer, sending integer array

I have an embedded C++ project where I'm reading a series of int32's from a hardware device, then packing them into an int array as part of large data structure, and then sending to a remote system over TCP/IP. So, I was using a simple data struct with a bunch of stuff defined and now I want to convert this to use Protocol Buffers. So, I was thinking of using a "repeated int32 data" as the element of my proto buff. But I want to avoid using a loop such as this:

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] );
}

I'd much rather use an efficient method, such as making the proto buff just point to the existing hardware_data[] array (a zero copy method), or using memcpy from hardware_data into proto.data.

I understand how to setup the memcpy(), but how then does the proto buff know how many elements are in the proto.data "array"? Can I still use the proto.data_size() to get the number of elements? Is there an efficient way to move the data from my hardware read to the proto buff for sending? Is there a better way to do this?

Kerrik, I wasn't aware of the zero copy API. Here's my proto definition:

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];
}

I'm not sure how the zero copy would play into this proto buff definition.

like image 650
rbwilliams Avatar asked Jan 07 '15 20:01

rbwilliams


People also ask

Does Protobuf handle endianness?

Protocol buffers messages always use little-endian encoding. Implementations running on big-endian architectures should be doing the conversions automatically. If you are receiving data in wrong order, I would suggest using protoc --decode_raw to see whether the error occurs on the transmission or reception side.

What is the difference between Proto2 and Proto3?

Proto3 is the latest version of Protocol Buffers and includes the following changes from proto2: Field presence, also known as hasField , is removed by default for primitive fields. An unset primitive field has a language-defined default value.

Should I use Proto2 and Proto3?

Proto2: supports optional natively, the wrapper types were recommended but should be avoided in new applications. Proto3: originally did not support presence tracking for primitive fields. As of 2020, proto3 supports both optional fields which have has_foo() methods and "singular" fields, which do not.

Is Protobuf faster than JSON?

It is essential to be aware that protobuf is not necessarily the fastest option. If your data is mainly string, then JSON format might be a good choice.


1 Answers

On the wire, a packed repeated int32 is encoded as a series of varints. A varint is a variable-width encoding in which smaller values take less space. Of course, this isn't how the data is represented in your array, so embedding it into the message zero-copy isn't really possible.

In fact, though, you're currently doing two copies, and you can eliminate one of them. Instead of allocating int hardware_data[1000] directly, consider sticking the data directly into a google::protobuf::RepeatedField<int>. You can then make clever use of Swap() to move that data into a message without a copy:

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

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

After you've serialized the message, you may wish to additionally Swap() the field back, so that you can reuse the memory that was already reserved. (RepeatedField::Clear() will not free the underlying memory, just mark it for reuse.)

With all that said, serializing the message will still require copying the data as part of encoding it. Even if you changed the encoding to packed repeated fixed32 (which is actually encoded as 32-bit integers on the wire), there's no way to convince the library to use your memory directly.

like image 71
Kenton Varda Avatar answered Sep 22 '22 22:09

Kenton Varda