2011-06-13 105 views
3

我的問題是一樣的,從五年前在this thread討論(沒有很好的答案)。是否可以重用binary_oarchive實例?

我序列化我的對象到一個字節的緩衝區,像這樣:

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個對象/秒。

我已經精確定位問題下降到binary_oarchive的娛樂。如果我在同一個循環中使用同一個歸檔實例寫入同一個字符串,我得到〜22萬個對象/秒,但是隨後,這些對象依次序列化,這不是我想要的:我想清除並在每個對象序列化後重用相同的緩衝區(尋找其開始)。

我該怎麼做?

+0

那是什麼,儘快爲他們」是具有所有的緩衝區,如果你打算扔掉其內容的點(相同緩衝區讀寫器之間共享)重寫? – 2011-06-13 11:42:41

+0

循環中究竟包含什麼?如果你聲明'serial_str',你也不必清除它,因爲它是在每個循環中創建的。 – 2011-06-13 11:48:57

+0

@Tomalak對不起,我應該提到序列化每個對象後,我使用boost :: interprocess :: message_queue將其內容發送到另一個進程。 – 2011-06-13 11:54:43

回答

2

是的,你絕對可以重複使用它,在一定意義上。該oarchive簡單包紮流,不知道發生了什麼事情與該流的數據,所以關鍵是要實現自己的流(這是不好玩),讓你「復位」的實際underlaying的數據流。我以前寫過類似的東西,它的功能非常好。

的一些陷阱是的知道,雖然:

的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是可能的,但它是一個有點疼痛。一旦你把它整理出來,那真是太棒了。

+0

謝謝,這非常有幫助!您能否擴展一下爲什麼我必須編寫自己的流,並且不能只重用boost庫中的一個,以及我如何才能構建它?我的自定義流將如何不同於說,一個std :: stringstream? – 2011-06-13 13:03:10

+0

如果您想將其指向不同的目標,則需要使用自己的流。對我而言,我在考慮文件,但它仍然適用於您的代碼。您需要爲每個新字符串創建一個新流,因此如果您不寫自己的流,這意味着您需要爲每個新字符串創建一個新的存檔。看看你的代碼,它表明你通過清除它來重用字符串,所以這就足夠了。如果你不得不切換到不同的字符串,那麼你需要一個自定義流來允許這個。但是,我對頭文件和類型註冊的描述仍然適用。 – 2011-06-13 13:09:29

+0

如果您的反序列化和序列化保持完全同步(因此不需要持久性存儲),那麼您甚至可以避免更改標題並處理類型註冊。但是,這是危險的,非常脆弱。 (假設你的字符串正在讀取另一邊的iarchive) – 2011-06-13 13:31:22

0

一個解決方案,而無需看多進一步將存儲字符串的長度最後,並獲得使用最後的長度與實際長度的字符串(將被添加到輸出的最後一個字符串)。每10或100次迭代,您可以重新啓動binary_oarchive,以不累積serial_str中過去的編碼對象。

+0

謝謝,我認爲,但似乎像那裏某種程度上應該是一個開箱即用的解決方案? – 2011-06-13 12:15:26

+0

好的,這裏的問題是'back_insert_device'不可搜索,所以你不能一開始就去尋找它。 – 2011-06-13 12:27:12

+0

哦,集成電路!我認爲boost :: iostreams :: basic_array是可搜索的,我會嘗試使用它來看看會發生什麼。 – 2011-06-13 13:04:13

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將傳遞的指針序列化到緩衝區。 讀者從同一緩衝區反序列化指針

#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"; }; 
}; 
相關問題