开发者

C++ portable array serialization

On a project I work on, I have to send arrays of float / double开发者_C百科 back and forth over a network, I'm using Boost.Asio for the network stuff, as I need the communication to be async, and that just seemed to be the best (the only real one?) out there...

The arrays sent are floats / doubles, and the type is known to both sides. AFAIK, there are possible problems with the floating point storage standards + the same stuff that goes on with longs/ints, endians etc.

Logically, the arrays sent are dense matrices, handled by Eigen on one end, using BLAS / [MKL|ATLAS] and whatnot on the other end, quite possible that other uses will be needed, so I'm going for the most generic way possible & that seems to be passing around arrays.

The key requirement is high performance and portability, as both the client and server could possibly be run on any combination of 32 / 64 bit and the communication is pretty intense (real-time monitoring, refreshes every-something-seconds for the client), so the serialization overhead itself has to be minimal.

From what I have found so far, the two big players to consider here are Boost.Serialization and Google's Protobuf.

The big pro for BS is that I use Boost quite alot already in the project (though unit testing is in Google Test) and it seems to be really simple to serialize the array using make_array(). The big con for it is performance.

The advantage for protobuf from what I found is performance, all the benches seem to show that it outperforms BS by a factor of 10-20 on ANY operation. What I didn't find in the protobuf docs though is adding an array to the message. It uses repeated fields, and from what I understand, I'd have to use MsgObject.repeatedProp.Add(const T&) on each element of the array, which would mean, i.e., 10k calls to it for a 10k array, and that seems kindof costly too.

Any suggestions on how to tackle this would be highly appreciated, as my experience with C++ is limited and I've only recently restarted writing in it after a longer break...


With protobufs, you can void the 10k calls to it for a 10k array if you encode the array using "bytes" instead of "repeated int32" (or similar). In your code, if you cast pointers and use memcpy, it is usually very fast.


In addition to boost asio, you might want to look at boost.MPI (message passing interface). This uses boost.serialization for the serialization behind the scenes.

Boost.mpi discusses the performance optimisations that are possible for booost.serialization. In particular, the use of

  • BOOST_CLASS_TRACKING(gps_position,track_never)
  • BOOST_CLASS_IMPLEMENTATION(gps_position,object_serializable)

macros, and the mpi-defined macro

  • BOOST_IS_MPI_DATATYPE(gps_position)

These optimisations seem to work well, see this graph.

I have never used protobuff so I can't speak for this.


There are good examples of this being done available on github. Here are some possible resources for going to and from protocol buffers eigen, and matrices:

  • HAL hardware abstraction library (robotics).
  • Tensorflow does much the same thing with tensors (nd array) but their implementation will likely be much more complex considering the expansive feature set they support.
  • NUbots has another example.
  • Chromium seems to also have examples.
  • ceres-solver used to support protobufs for matrices but that functionality has since been removed. (header)

Here is the HAL version, which is simple and looks like it will do pretty well:

Protobuf for storing a matrix:

package hal;

message MatrixMsg {
  required uint32 rows = 1;
  // columns deduced by division. Data stored as column major
  repeated double data = 2 [packed=true];
}

message VectorMsg {
  repeated double data = 1 [packed=true];
}

Loading into eigen and writing to protobuf:

#pragma once

#include <Eigen/Eigen>
#include <HAL/Messages.pb.h>

namespace hal {

inline void ReadMatrix(const MatrixMsg &msg, Eigen::MatrixXd* mat) {
  mat->resize(msg.rows(),msg.data_size()/msg.rows());
  for(int ii = 0 ; ii < msg.data_size() ; ii++){
    mat->operator()(ii) = msg.data(ii);
  }
}

inline void ReadVector(const VectorMsg &msg, Eigen::VectorXd* vec) {
  vec->resize(msg.data_size());
  for(int ii = 0 ; ii < msg.data_size() ; ii++){
    vec->operator()(ii) = msg.data(ii);
  }
}

inline void WriteMatrix(const Eigen::MatrixXd &mat, MatrixMsg *msg) {
  msg->set_rows(mat.rows());
  msg->mutable_data()->Reserve(mat.rows()*mat.cols());
  for(int ii = 0 ; ii < mat.cols()*mat.rows() ; ii++){
    msg->add_data(mat(ii));
  }
}

inline void WriteVector(const Eigen::VectorXd &mat, VectorMsg *msg) {
  msg->mutable_data()->Reserve(mat.rows());
  for(int ii = 0 ; ii < mat.rows() ; ii++){
    msg->add_data(mat(ii));
  }
}

}  // namespace hal

However if you're going to do it across libraries there is more you'll have to account for. There was one other library that had a good implementation, where there were enums for the types, width, height and then a byte array, rowmajor/colmajor etc. However I cannot locate it again. These will have to be accounted for and included in the protobuf to be compatible across matrix libraries. For some ideas on how to do this the HAL image protobuf and image cpp may help, which read/write from opencv sources which can also be considered to be a matrix.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜