4

私の質問は、 5年前のこのスレッドで説明したものと同じです(これには良い答えはありません)。

次のように、オブジェクトをバイトバッファにシリアル化します。

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
}    

これをループで実行すると、パフォーマンスがかなり悪くなります。1秒あたり最大14,000個のオブジェクトを取得します。

私はbinary_oarchiveの再現に至るまで問題を特定しました。ループ内の同じアーカイブインスタンスで同じ文字列に書き込むと、最大220,000オブジェクト/秒が得られますが、オブジェクトは次々にシリアル化されます。これは私が望むものではありません。クリアしたい各オブジェクトがシリアル化された後、同じバッファーを再利用します(最初までシークします)。

どうやってやるの?

4

3 に答える 3

2

はい、ある意味では絶対に再利用できます。oarchiveは単にストリームをラップし、ストリームのデータで何が起こっているのかわからないため、実際のアンダーレイデータストリームを「リセット」できるように独自のストリームを実装するのがコツです(これは面白くありません)。私は以前にこのようなものを書いたことがあり、それは素晴らしい働きをします。

ただし、注意すべきいくつかの落とし穴:

oarchiveはヘッダー情報を書き続けないので(それが持続する場合はすべてを1つの大きなストリームとして扱うため)、ヘッダーを無効にする必要があります。

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 :: vector、それらのすべてのstd::shared_ptrsなど。これは非常に重要です。それ以外の場合は、共有iarchiveを使用し、シリアル化されたのとまったく同じ順序でストリームを読み取る場合にのみ、ストリームを読み戻すことができます。

その結果、iarchiveは、oarchiveとまったく同じ方法と順序ですべてのタイプを登録する必要があります(これを支援するためにmplを使用して便利なヘルパーを作成しました)。

iarchiveを介してシリアル化して戻すことも、同じiarchiveを共有できますが、すべて同じ条件が適用されます。

  • 独自のストリームを作成する必要があります(リダイレクト/リセットできるようにするため)
  • アーカイブヘッダーを無効にする
  • レジスタタイプを持っている

そうです、oarchive / iarchiveを再利用することは可能ですが、それは少し面倒です。あなたがそれを整理したら、それはかなり素晴らしいです。

于 2011-06-13T12:57:35.433 に答える
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);

ストリーム内のいくつかの内部ポインタを変更するのと同じくらい高速である必要があります。ただし、実際の速度はテストしていません。

これを試すためのコード:ライターは渡されたポインターをバッファーにシリアル化します。リーダーは同じバッファーからポインターを逆シリアル化します(同じバッファーがリーダーとライターの間で共有されます)

#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"; };
};
于 2013-09-23T15:34:12.803 に答える
0

さらに詳しく調べる必要のない1つの解決策は、文字列の最後の長さを格納し、最後の長さと実際の長さ(出力に追加される最後の文字列)を使用してサブ文字列を取得することです。10回または100回の反復ごとに、再起動して、binary_oarchive過去にエンコードされたオブジェクトを多く蓄積しないようにすることができますserial_str

于 2011-06-13T12:04:27.667 に答える