是否可以重用binary_oachive实例

Is it possible to reuse a binary_oarchive instance?

本文关键字:oachive 实例 binary 是否      更新时间:2023-10-16

我的问题与五年前在这个线程中讨论的问题相同(没有很好的答案)。

我正在将我的对象序列化到字节缓冲区中,如下所示:

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
}    

当我在一个循环中这样做时,性能相当糟糕:我得到大约14000个对象/秒

我已经把问题归结为二进制的再创造。如果我只是在循环中用相同的归档实例写入相同的字符串,我会得到大约220000个对象/秒,但随后,对象会依次序列化,这不是我想要的:我想在每个对象序列化后清除并重用相同的缓冲区(从其开始)。

我该怎么做?

是的,从某种意义上说,您绝对可以重用它。oarchive只是简单地封装了一个流,不知道流的数据发生了什么,所以诀窍是实现你自己的流(这并不有趣),让你"重置"实际的底层数据流。我以前写过这样的东西,效果很好。

不过,需要注意的一些问题:

oarchive不会一直写出标题信息(因为如果它持续存在,它会将所有内容都视为一个大流),所以你会想禁用标题:

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

此外,因为您正在重用oarchive,所以在管理其内部类型表时必须格外小心。如果您正在序列化的都是int、float等,那么您就可以了,但一旦您开始序列化类、字符串等,您就不能依赖于归档在像这样重用归档时使用的默认类型枚举。Boost文档并没有真正涉及到这一点,但对于任何复杂的内容,您需要对存档遇到的每个类型执行以下操作:

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

等等…对于所有类型,它们的所有std::vectors,它们的全部std::shared_ptr,等等。这是至关重要的。否则,只有使用共享iarchive并按照与序列化时完全相同的顺序读取流,才能读回流。

结果是,你的iarchive需要以与他们的oarchive完全相同的方式和顺序注册所有类型(我用mpl写了一些方便的助手来帮助我)。

通过iarchive序列化返回也可以共享相同的iarchive,但是所有相同的条件都适用:

  • 您需要编写自己的流(以便可以重定向/重置)
  • 禁用存档标头
  • 具有寄存器类型

所以,是的,重复使用桨叶是可能的,但这有点痛苦。不过,一旦你把它整理好,它就非常棒了。

这是我提出的解决方案。它不需要实现自己的流,并且允许在每次下一次序列化中重用相同的内存块。假设您已经安排了以下结构进行序列化:

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

应该和更改流中的一些内部指针一样快。不过,我还没有测试实际速度。

尝试此操作的代码:写入程序序列化传递到缓冲区的指针。读取器反序列化来自同一缓冲区的指针(读取器和写入器之间共享相同的缓冲区)

#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 okn";
    // test reuse
    w.reset();
    r.reset();
    A *a3 (NULL);
    w.write(new A(10));
    a3 = r.read();
    assert(*a3 == A(10));
    std::cout << "Reuse okn";
};
class A
{
private:
  friend class boost::serialization::access;
  int i;
  template <typename Archive>
  void serialize(Archive& ar, const unsigned int version) {
    std::cout << "serialize An";
    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"; };
};

一个解决方案是存储字符串的最后长度,并使用最后长度和实际长度(将是添加到输出中的最后一个字符串)来获取子字符串。每10次或100次迭代,您都可以重新启动binary_oarchive,以避免在serial_str中积累太多过去的编码对象。