C ++ переносимая сериализация массивов - PullRequest
3 голосов
/ 21 июля 2011

В проекте, над которым я работаю, я должен отправлять массивы float / double назад и вперед по сети, я использую Boost.Asio для сетевых вещей, так как мне нужно, чтобы связь была асинхронной, и это просто казалось лучшим (единственным реальным?) там ...

Отправленные массивы являются числами с плавающей запятой / двойными числами, и тип известен обеим сторонам. AFAIK, возможны проблемы со стандартами хранения с плавающей запятой + те же вещи, что и с long / ints, endian и т. Д.

Логически, отправленные массивы представляют собой плотные матрицы, обрабатываемые Eigen на одном конце, используя BLAS / [MKL | ATLAS] и еще много чего на другом конце, вполне возможно, что понадобятся другие варианты использования, поэтому я собираюсь перейти к самый общий возможный способ, и кажется, что он обходит массивы.

Ключевым требованием является высокая производительность и портативность, так как клиент и сервер могут работать на любой комбинации 32/64 бит, а связь довольно интенсивная (мониторинг в режиме реального времени обновляет каждые несколько секунд для клиент), поэтому издержки сериализации должны быть минимальными.

Из того, что я нашел до сих пор, два крупных игрока, которые следует рассмотреть здесь, это Boost.Serialization и Google Protobuf.

Большим плюсом для BS является то, что я использую Boost довольно часто уже в проекте (хотя юнит-тестирование в Google Test), и, кажется, действительно просто сериализовать массив с помощью make_array(). Большой плюс к этому - производительность.

Преимущество для protobuf в том, что я обнаружил, заключается в производительности: все стенды, кажется, показывают, что он превосходит BS в 10-20 раз при ЛЮБОЙ работе. Что я не нашел в документации по protobuf, так это добавление массива к сообщению. Он использует повторяющиеся поля, и из того, что я понимаю, я должен был бы использовать MsgObject.repeatedProp.Add(const T&) для каждого элемента массива, что означало бы, то есть, вызовы 10k для него для массива 10k, и это тоже довольно дорого.

Любые предложения о том, как решить эту проблему, будут высоко оценены, так как мой опыт работы с C ++ ограничен, и я только недавно возобновил запись в нем после длительного перерыва ...

Ответы [ 3 ]

1 голос
/ 12 февраля 2012

С помощью protobufs вы можете аннулировать вызовы 10k для массива 10k, если кодируете массив, используя «байты» вместо «repeat int32» (или аналогичного). В вашем коде, если вы используете указатели и используете memcpy, это обычно очень быстро.

1 голос
/ 21 июля 2011

В дополнение к boost asio вы можете посмотреть на boost.MPI (интерфейс передачи сообщений).Это использует boost.serialization для сериализации за кулисами.

Boost.mpi обсуждает оптимизацию производительности, которая возможна для booost.serialization.В частности, использование

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

макросы и макрос, определенный в mpi

  • BOOST_IS_MPI_DATATYPE (gps_position)

Кажется, что эти оптимизации работают хорошо, смотрите этот график.

Я никогда не использовал protobuffпоэтому я не могу говорить за это.

0 голосов
/ 12 ноября 2016

Есть хорошие примеры того, как это делается на github.Вот некоторые возможные ресурсы для перехода в собственные буферы протокола и из них, а также матрицы:

  • HAL библиотека аппаратных абстракций (робототехника).
  • Tensorflow делает то же самое с тензорами (nd-массив), но их реализация, вероятно, будет гораздо более сложной, учитывая расширяемый набор функций, который они поддерживают.
  • NUbots имеет другой пример.
  • Хром , кажется, также имеет примеры.
  • ceres-solver используется для поддержки protobufs для матриц , но эта функциональность имеетс тех пор был удален.( header )

Вот версия HAL, которая проста и выглядит так, как будто она будет работать очень хорошо:

Protobuf для хранения матрицы:

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

Загрузка в eigen и запись в 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

Однако, если вы собираетесь делать это в разных библиотеках, есть ещевам придется учитывать.Была еще одна библиотека, которая имела хорошую реализацию, где были перечисления для типов, ширины, высоты, а затем байтовый массив, rowmajor / colmajor и т. Д. Однако я не могу найти его снова.Они должны быть учтены и включены в protobuf для совместимости с матричными библиотеками.Для некоторых идей о том, как это сделать, могут помочь HAL image protobuf и image cpp , которые читают / пишут из исходников opencv, которые также можно рассматривать как матрицу.

...