C++ 是否可以重用二进制软件实例?
我的问题与五年前讨论的相同(没有很好的答案) 我将对象序列化到字节缓冲区中,如下所示:C++ 是否可以重用二进制软件实例?,c++,performance,boost,boost-serialization,C++,Performance,Boost,Boost Serialization,我的问题与五年前讨论的相同(没有很好的答案) 我将对象序列化到字节缓冲区中,如下所示: 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_devic
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
}
std::string serial\u str;
对于(i=1;i<10000;i++)
{
boost::iostreams::back\u insert\u设备插入器(串行\u str);
boost::iostreams::流s(插入器);
boost::archive::binary_oarchive oa(s);
oa一种解决方案,无需进一步查看,就是存储字符串的最后一个长度,并使用最后一个长度和实际长度(将是添加到输出中的最后一个字符串)获取子字符串。每10或100次迭代,您就可以重新启动二进制\u oarchive
,而不必在串行\u str中积累太多过去的编码对象。是的,从某种意义上说,您完全可以重用它。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> >();
oa.template register_type();
oa.template register_type();
oa.template register_type();
以此类推。对于所有类型,所有的std::vectors,所有的std::shared\u ptr,等等。这是至关重要的。否则,只有使用共享iChive并以与序列化完全相同的顺序读取流,才能读回流
结果是,您的iChive需要以与Oachive完全相同的方式和顺序注册所有类型(我编写了一些使用mpl的方便助手来帮助我)
通过iAchive重新序列化也可以共享相同的iAchive,但是所有相同的条件都适用:
- 您需要编写自己的流(以便可以重定向/重置)
- 禁用存档标题
- 有寄存器类型吗
是的,重复使用oarchive/iRChive是可能的,但这有点痛苦。但一旦你把它整理好,它就非常棒了。这是我想出的解决方案。
它不需要实现您自己的流,并且允许为下一次序列化重用相同的内存块。
假设您为序列化安排了以下结构:
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
应该和在流中更改一些内部指针一样快。尽管如此,我还没有测试实际速度
用于尝试此操作的代码:
写入程序序列化传递给缓冲区的指针。
读卡器从同一缓冲区反序列化指针(读卡器和写卡器共享同一缓冲区)
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
甲级;
班主任{
char*buf;
内伦;
boost::iostreams::basic_阵列接收器;
boost::iostreams::stream操作系统;
boost::archive::binary_oarchive oa;
公众:
写入程序(char*_-buf,int-len):buf(_-buf),len(_-len),sink(buf,len),os(sink),oa(os){}
无效写入(A*A){
oa>a;
返回a;
}
无效重置(){
is.close();
is.open(src);
}
};
int main(int argc,字符**argv){
//记起
字符缓冲区[4096]={0};
写入器w(缓冲区,sizeof(缓冲区));
A*a1=新的A(5);
w、 写入(a1);
读卡器r(缓冲区,sizeof(缓冲区));
A*a2(空);
a2=r.read();
断言(*a1==*a2);
std::cout那么,如果要在写入内容后立即丢弃其内容,那么拥有缓冲区有什么意义呢?循环中到底包含了什么?如果声明serial\u str
也不必清除它,因为它是在每个循环中创建的。@Tomalak抱歉,我应该在序列化每个对象之后提到,我正在使用boost::进程间::消息队列将其内容发送到另一个进程。@Omer:Aha!是的,您应该有。:)@Diego@Tomalak我很抱歉,我已经更新了我的代码来解决这些问题。谢谢,我考虑过了,但似乎应该有一个现成的解决方案?好吧,这里的问题是back\u insert\u device
不可查找,所以你不能一开始就查找。哦,ic!我认为boost::iostreams::basic\u数组是可查找的,我不会我想用它来代替,看看会发生什么。是的,我正要提出这个建议,但我没有boost streams方面的经验。另外,请注意,创建二进制文件并不那么昂贵,因为它只使用给定流中的streambuf(存储对它的引用),因此,只需缠绕流并创建一个新的binary\u oarchive
即可保持性能。谢谢,这非常有帮助!您能否详细介绍一下为什么我必须编写自己的流,而不能只重用boost库中的流,以及如何着手构建它?我的自定义流与std::stringstr有什么不同eam?如果要将流指向不同的目标,您需要使用自己的流。对我来说,我考虑了文件,但它仍然适用于您的代码。每个新字符串都需要一个新流,因此如果您不编写自己的流,这意味着每个新字符串都需要一个新的oarchive。查看您的代码,这表明您正在重用strin通过清除它,这样就足够了
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 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"; };
};