Можно ли повторно использовать экземпляр binary_oarchive? - PullRequest
3 голосов
/ 13 июня 2011

Мой вопрос такой же, как обсуждалось в этой теме от пяти лет назад (на который нет хорошего ответа).

Я сериализую свои объекты в байтовый буфер, вот так:

std::string serial_str;
for (i = 1; i < 10000; i++)
{
    boost::iostreams::back_insert_device<std::string> inserter(serial_str);
    boost::iostreams::stream<boost::iostreams::back_insert_device<std::string> > s(inserter);
    boost::archive::binary_oarchive oa(s);

    oa << obj;

    s.flush();

    // code to send serial_str's content to another process, omitted.

    serial_str.clear(); // clear the buffer so it can be reused to serialize the next object
}    

Когда я делаю это в цикле, производительность довольно плохая: я получаю ~ 14 000 объектов / сек.

Я точно определил проблему до воссоздания двоичного_архива. Если я просто записываю одну и ту же строку с тем же экземпляром архива в цикле, я получаю ~ 220 000 объектов в секунду, но затем объекты последовательно сериализуются один за другим, а это не то, что я хочу: я хочу очистить и повторно использовать тот же буфер (искать его начало) после сериализации каждого объекта.

Как я могу это сделать?

Ответы [ 3 ]

2 голосов
/ 13 июня 2011

Да, вы, безусловно, можете использовать его в некотором смысле. Oarchive просто оборачивает поток и не знает, что происходит с данными потока, поэтому хитрость заключается в том, чтобы реализовать свой собственный поток (что не весело), ​​чтобы позволить вам «сбросить» фактический лежащий в основе поток данных. Я уже писал что-то подобное, и это прекрасно работает.

Некоторые ошибки, о которых следует знать:

Oarchive не будет продолжать записывать информацию заголовка (поскольку, если он сохраняется, он обрабатывает все как один большой поток), поэтому вы захотите отключить заголовки:

boost::archive::binary_oarchive oa(s, boost::archive::no_codecvt | boost::archive::no_header);

Кроме того, поскольку вы повторно используете oarchive, вы должны быть чрезвычайно осторожны при управлении его внутренней таблицей типов. Если все, что вы сериализуете - это целые числа, числа с плавающей запятой и т. Д., То все будет в порядке, но как только вы начнете сериализовать классы, строки и тому подобное, вы не сможете полагаться на перечисление типов по умолчанию, которое архив использует при повторном использовании. архив такой. Документация Boost на самом деле не касается этого, но для чего-то сложного вам нужно сделать следующее для каждого типа, с которым столкнется архив:

oa.template register_type<std::string>();
oa.template register_type<MyClass>();
oa.template register_type<std::shared_ptr<MyClass> >();

И так далее ... для всех ваших типов, всех их std :: vectors, всех их std :: shared_ptrs и т. Д. Это vital . В противном случае вы сможете читать потоки только в том случае, если вы используете общий архив и читаете их в том же порядке, в котором они были сериализованы.

Следствием этого является то, что вашему iarchive необходимо регистрировать все типы точно таким же образом и в том же порядке, что и их архив (я написал несколько удобных помощников, использующих mpl, чтобы помочь мне в этом).

Сериализация обратно через iarchive также может использовать тот же самый iarchive, однако применяются все те же условия:

  • Вам нужно написать собственный поток (чтобы его можно было перенаправить / сбросить)
  • Отключить архив заголовков
  • Есть регистр типов

Так что да, повторное использование архива / архива возможно, но это немного болезненно. Как только вы разберетесь с этим, это будет круто.

2 голосов
/ 23 сентября 2013

Вот решение, которое я придумал.Он не требует реализации вашего собственного потока и позволяет повторно использовать один и тот же кусок памяти для каждой следующей сериализации.Предполагается, что у вас есть следующие структуры, организованные для сериализации:

boost::iostreams::basic_array<char> sink; // target buffer 
boost::iostreams::stream<boost::iostreams::basic_array<char> > os;  // stream wrapper around it
boost::archive::binary_oarchive oa;  // archive which uses this stream

Затем для повторного использования того же буфера просто откройте поток:

os.close();
os.open(sink);

Должно быть так же быстро, как изменить некоторые внутренние указатели внутрипоток.Я не проверял фактическую скорость, хотя.

Код для проверки: Writer сериализует переданный указатель на буфер.Считыватель десериализует указатель из одного и того же буфера (один и тот же буфер используется для чтения и записи)

#include <iostream>
#include <fstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/access.hpp>

class A;
class Writer {
    char *buf;
    int len;
    boost::iostreams::basic_array<char> sink;
    boost::iostreams::stream<boost::iostreams::basic_array<char> > os;
    boost::archive::binary_oarchive oa;
public:
    Writer(char *_buf, int _len): buf(_buf), len(_len), sink(buf, len), os(sink), oa(os) {}
    void write(A* a) {
        oa << a;
    }
    void reset() {
        os.close();
        os.open(sink);
    }
};
class Reader {
    char *buf;
    int len;
    boost::iostreams::basic_array_source<char> src;
    boost::iostreams::stream<boost::iostreams::basic_array_source<char> > is;
    boost::archive::binary_iarchive ia;
public:
    Reader(char *_buf, int _len): buf(_buf), len(_len), src(buf, len), is(src), ia(is) {}
    A* read() {
        A* a;
        ia >> a;
        return a;
    }
    void reset() {
        is.close();
        is.open(src);
    }
};

int main(int argc, char **argv) {
    // to memory
    char buffer[4096] = {0};

    Writer w(buffer, sizeof(buffer));
    A *a1 = new A(5);
    w.write(a1);

    Reader r(buffer, sizeof(buffer));
    A *a2 (NULL);
    a2 = r.read();

    assert(*a1 == *a2);
    std::cout << "Simple ok\n";

    // test reuse
    w.reset();
    r.reset();

    A *a3 (NULL);
    w.write(new A(10));
    a3 = r.read();

    assert(*a3 == A(10));
    std::cout << "Reuse ok\n";
};

class A
{
private:
  friend class boost::serialization::access;
  int i;

  template <typename Archive>
  void serialize(Archive& ar, const unsigned int version) {
    std::cout << "serialize A\n";
    ar & i;
  }
public:
  A(): i(0) {};
  A(int _i): i(_i) {};
  virtual bool operator==(const A&r) { return i == r.i; };

  virtual ~A() {};
  virtual void whoa() {std::cout << "I am A!\n";};
  virtual const char* me() { return "A"; };
};
0 голосов
/ 13 июня 2011

Одним из решений, без необходимости заглядывать дальше, было бы сохранение последней длины строки и получение подстроки с использованием последней длины и фактической длины (будет последней строкой, добавленной к выходным данным).Каждые 10 или 100 итераций вы можете перезапускать binary_oarchive, чтобы не накапливать много прошлых закодированных объектов в serial_str.

...