我的问题与五年前this thread中所讨论的问题相同(没有好的答案)。
我正在将对象序列化为字节缓冲区,如下所示:
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个对象/秒。
我已经将这个问题指向了binary_oarchive。如果我只是在一个循环中用相同的归档实例写入相同的字符串,我得到~220,000个对象/秒,但是,然后,这些对象依次序列化,这不是我想要的:我想要清除并且在每个对象被序列化之后重用相同的缓冲区(寻找它的开头)。
我该怎么做?
答案 0 :(得分:2)
是的,从某种意义上说,你绝对可以重复使用它。 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 ::向量,它们的所有std :: shared_ptrs等等。这是至关重要的。否则,如果您使用共享的iarchive,则只能读回您的流,并按照序列化的顺序读取它们。
结果是你的iarchive需要以与它们的oarchive完全相同的方式和顺序注册所有类型(我使用mpl编写了一些方便的助手来帮助我)。
通过iarchive序列化也可以共享相同的iarchive,但是所有相同的条件都适用:
所以是的,重用oarchive / iarchive是可能的,但这有点痛苦。一旦你把它整理好了,它就非常棒了。
答案 1 :(得分:2)
这是我提出的解决方案。 它不需要实现您自己的流,并允许为每个下一个序列化重用相同的内存块。 假设您有以下结构用于序列化:
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序列化传递指针到缓冲区。 Reader从同一个缓冲区反序列化指针(读写器之间共享相同的缓冲区)
#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"; };
};
答案 2 :(得分:0)
一个解决方案,无需进一步查看,将存储字符串的最后一个长度,并使用最后的长度和实际长度获取子字符串(将是添加到输出的最后一个字符串)。每次10或100次迭代,您可以重新启动binary_oarchive
,而不是在serial_str
中累积过多的编码对象。