是否可以重用binary_oachive实例
Is it possible to reuse a binary_oarchive instance?
我的问题与五年前在这个线程中讨论的问题相同(没有很好的答案)。
我正在将我的对象序列化到字节缓冲区中,如下所示:
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
中积累太多过去的编码对象。
- 从C++实例化QML
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 无法创建抽象类的实例
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 我收到以下错误:抛出'std::bad_alloc'实例后终止调用
- 建议在运行时将带有类实例的列表从c++导入qml
- 约束和显式模板实例化
- 通过实例理解std::move及其目的
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 有没有一种"cleaner"的方法可以在指向基的指针向量中找到派生类的第一个实例?
- 正在生成未知类实例
- 从DLL中删除类的实例
- 在std::vector上存储带有模板的类实例
- 为什么创建友元类的实例会导致"undefined reference to"错误?