2011-06-13 4 views
3

제 질문은 5 년 전 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 
}  

, 성능이 아주 나쁜 : 나는 ~ 14,000 개체/초 얻을.

나는이 문제를 binary_oarchive의 레크리에이션으로 지적했다. 루프에 같은 아카이브 인스턴스가있는 동일한 문자열에 쓰기 만하면 ~ 220,000 개/초의 객체가 생기지 만 객체는 순차적으로 하나씩 차례로 직렬화됩니다. 이는 원하는 것이 아닙니다. 각 객체가 직렬화 된 후 동일한 버퍼를 재사용합니다 (처음으로 탐색).

어떻게하면됩니까?

+0

그런 다음 즉시이 '당신이 그 내용을 버릴려고하는 경우에, 모든 버퍼를 갖는 지점 무엇 (같은 버퍼가 독자와 작가 사이에 공유) 같은 버퍼 포인터를 역 직렬화 다시 썼나? –

+0

정확히 루프에 무엇이 포함되어 있습니까? 'serial_str'을 선언하면 각 루프에서 생성되기 때문에 지울 필요가 없습니다. –

+0

@Tomalak 죄송합니다. 각 객체를 직렬화 한 후에 boost :: interprocess :: message_queue를 사용하여 내용을 다른 프로세스로 보냅니다. –

답변

2

예, 어떤 의미에서는 절대적으로 재사용 할 수 있습니다. oarchive는 단순히 스트림을 감싸고 스트림의 데이터가 무엇인지 알지 못하기 때문에 실제 언더 레이 데이터 스트림을 "재설정"할 수 있도록 자신 만의 스트림을 구현하는 것이 즐겁지는 않습니다. 나는 전에 이런 것을 썼고 그것은 훌륭하게 작동합니다.

어떤 개는 인식하지만이 될하기 :

헤더 정보를 계속 쓸 것 oarchive 당신이 헤더를 사용하지 않을 것이다, 그래서 (그것이 하나 개의 큰 스트림으로 모든 것을 처리하는 것 지속되면 이후) : 당신이 oarchive를 재사용하고 있기 때문에

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

는, 당신은 내부 형 테이블을 관리하는 방법에 대한 매우 조심해야합니다. 직렬화하는 것이 모두 int, float 등인 경우에는 괜찮습니다.하지만 클래스, 문자열 등을 직렬화하는 즉시 재사용 할 때 아카이브가 사용하는 기본 유형 열거에 의존 할 수 없습니다 이런 아카이브. 부스트 문서는 정말이 들어가지만, 아무것도 복잡하지 않습니다, 당신은 아카이브가 건너 올 것이다 모든 유형 을 위해 다음을 수행해야합니다 모든 종류의 등

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

그리고 .. 모든 std :: vector, 그들 중 모든 std :: shared_ptrs 등이 있습니다. 이것은 vital입니다. 그렇지 않으면 공유 iarchive를 사용하고 직렬화 된 동일한 순서로 읽는 경우에만 스트림을 다시 읽을 수 있습니다.

결과적으로 iarchive는 모든 유형을 정확하게 동일한 방식으로 등록하고 해당 항목을 oarchive로 등록해야합니다 (필자는 mpl을 사용하여이 기능을 도와 줌).

또한, 같은 iarchive를 공유 할 수있는 iarchive을 통해 다시 일렬 그러나 모두 같은 조건이 적용

  • 당신은
  • 사용 안 함 (/ 그래서이 리디렉션 할 수 있습니다 재설정) 자신의 스트림을 작성해야 아카이브 헤더
  • 레지스터 유형

그래서 예, oarchive를 재사용 가능/iarchive입니다을 가지고 있지만 그것은 고통의 비트입니다. 일단 당신이 그것을 분류 했더라면, 그것은 꽤 굉장하다.

+0

감사합니다. 매우 도움이됩니다. 왜 내가 자신의 스트림을 써야만하는지, 그리고 boost 라이브러리에서 하나만 재사용 할 수는 없습니까? 그리고 어떻게 구축 할 수 있습니까? 내 사용자 정의 스트림은 std :: stringstream과 어떻게 다릅니 까? –

+0

다른 대상을 지정하려면 자체 스트림을 사용해야합니다. 필자는 파일을 생각하고 있었지만 여전히 코드에 적용됩니다. 각각의 새 문자열에 대해 새 스트림이 필요하므로 자신의 스트림을 작성하지 않으면 새 문자열 각각에 대해 새 저장소가 필요하다는 의미입니다. 코드를 보면 문자열을 지워서 문자열을 재사용한다는 의미이므로 충분합니다. 다른 문자열로 전환해야한다면이를 허용하기 위해 맞춤 스트림이 필요합니다.그러나 헤더 및 유형 등록에 대해 언급 한 내용은 여전히 ​​적용됩니다. –

+0

역 직렬화 및 직렬화가 완벽하게 동기화되어 (영구 저장 장치가 없기 때문에) 곧바로 out-in을 사용하면 헤더를 변경하거나 형식 등록을 처리하지 않을 수도 있습니다. 그러나 이것은 위험하고 매우 약합니다. (당신은 문자열의 반대편에 iarchive가 있다고 가정합니다) –

0

훨씬 더 살펴볼 필요가없는 한 가지 해결책은 문자열의 마지막 길이를 저장하고 마지막 길이와 실제 길이 (출력에 추가 된 마지막 문자열이 됨)를 사용하여 부분 문자열을 얻는 것입니다. 10 회 또는 100 회 반복 할 때마다 binary_oarchive을 다시 시작하여 serial_str에 과거의 인코딩 된 오브젝트가 많이 축적되지 않도록 할 수 있습니다.

+0

감사합니다. 어떻게 든 즉시 사용 가능한 솔루션이되어야합니까? –

+0

OK, 여기서 문제는'back_insert_device'가 탐색 가능하지 않기 때문에 처음으로 탐색 할 수 없다는 것입니다. –

+0

오, ic! 나는 boost :: iostreams :: basic_array가 seekable이라고 생각하고, 대신 그것을 사용하려고 시도하고 어떤 일이 일어나는지 보게 될 것이다. –

2

다음은 내가 생각해 낸 해결책입니다. 자체 스트림을 구현할 필요가 없으며 다음 번 serialization마다 동일한 메모리 덩어리를 다시 사용할 수 있습니다. 는 직렬화 구조체를 다음 한 것으로 추측 :

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"; }; 
}; 
관련 문제