2012-02-12 12 views
4

私はC++を使用していますが、参照カウントされたオブジェクトを使って大きなグラフを表現しています。ような何か:C++の低速再帰デストラクタコール

// Public class 
class Pointer{ 
    public: 
    // Constructors 
    Pointer(...){ node = ...; node->counter++;} 

    // Copy constructor 
    Pointer(Pointer& const other){ node = other.node; node->counter++;} 

    // Destructor 
    ~Point(){ if(--node->counter==0) delete node;} 

    // Other methods 
    ... 

    Node* node; 
}; 

// Node base class 
class Node{ 
    // Constructor 
    Node():counter(0){} 

    // Destructor 
    virtual ~Node(){} 

    // public: 
    unsigned long counter; 
}; 

// Node derived class 
class NodeDerived : public Node{ 
    // Constructors and other methods 
    ... 

    // Destructor 
    virtual ~NodeDerived(){ ... } 

    // Children 
    Pointer children[2]; 
}; 

多型クラスのノードへのポインタを含むパブリッククラスPointerとされています。 Nodeから派生したクラスには、新しいPointerインスタンスが含まれます。いくつかのPointerインスタンスが同じNodeインスタンスを指している可能性があることに注意してください。

私はこのクラスを使用して、数百万のインスタンスを持つ非常に大きなグラフを構築しています。これはうまくいきます。この問題は、グラフを削除するときに発生します。デフォルトのデストラクタ実装では、スタック・オーバーフローが発生します。これは、ルート・ノードのデストラクタでは、子のデストラクタがコールされ、子のデストラクタでは、子の子のデストラクタがコールされるなどです。

上記のNodeDerivedクラスのデストラクタを実装することで、スタックを使用して再帰呼び出しを避けることでこれを解決しました。これは動作しますが、まだ非常に遅いですし、私はこれをスピードアップする方法を探しています。どうやらメモリリークを引き起こすことなくデストラクタ宣言を避けることが可能ですか?virtual

答えて

2

破壊中に数百万のオブジェクトとスタックオーバーフローを処理する場合、重複する値を許可せず繰り返しをサポートするいくつかのタイプのコンテナ内のすべてのノードオブジェクトへのポインタを格納し、 std::setまたはstd::unordered_setのようになります。次に、グラフノード自体では、接続されたグラフノードへのポインタのみを格納します。グラフの削除時には、ノードを削除するために各グラフノードのポインタを再帰的に実行しないでください...代わりに、グラフ全体のノードセットへのポインタを含むコンテナに移動し、コンテナを反復処理します各ノードを1つずつ破棄します。グラフノードが接続されているすべてのノードを破壊するために、各グラフノードのポインタセットに「追従する」必要がないので、再帰は必要ありません。 std::shared_ptrのスペースと時間のオーバーヘッドについても心配する必要はありません。

+0

確かに、繰り返しによって再帰を置き換えますが、別のデータ構造が必要です。これは探検の良い方向のように聞こえる。 –

+0

@Jason。さて、私は別のストレージスキームについて考えていました。おそらく 'Pointer'クラスにある種のハッシュインデックスが含まれていましたが、私はすでに多くのストレージスキームを試しました。これはこれまでのところ、 。 – Joel

+0

'std :: set'は、オブジェクトを見つけるのに一定の時間を要しないのでオプションではありません。私たちのプログラムはまだC++ 0Xを使用できないので、' std :: unordered_set'もありません。私が 'std :: shared_ptr'の速度とオーバーヘッドを心配する必要はないと私の場合は真実ではありません。私はSTLに入ってから試してみませんでしたが、Boostバージョン(2年ほど前)を使用したときには遅すぎてメモリを使いすぎました。実際、Boost 'shared_ptr 'での私の経験は、私がこのクラスを最初に書くように導いてくれました。 – Joel

2

ノードを個別に削除しない場合は、参照カウントの代わりにmemory poolを使用することを検討してください。メモリプールを使用すると、グラフ全体が急速に吹き飛ばされます(プールの種類によっては、グラフ全体が破棄されるまで個々の削除されたオブジェクトが使用するメモリを回復できない可能性があります)。

+0

グラフのサブセットを効率的に削除する必要があります。ノードは継続的に追加され、削除されます。 – Joel

2

メモリリークを起こすことなくデストラクタを仮想宣言しないようにすることはできますか?

私は、ポインタの静的な型と一致しない指示先の動的な型とポインタにdelete pを呼び出すときに、より正確に、あなたは未定義の動作を避けることができないではない怖いです。

が、私はそれはそれは方法だ怖いまだ

非常に遅いです。数百万のオブジェクトを削除するのに時間がかかるでしょう。あなたはそれを回避することができます。例えば個々のオブジェクトの破壊を避けることによって、それらをすべてプールに入れて(そして、それを全体として)廃棄します。あなたの破壊的なニーズが何であるかによって異なります。

+0

あなたはおそらく正しいでしょう。私は削除プロセスをタイムアウトし、その削除の時間がノードあたり200 nsのようなものであることを見た。しかし、多くのノードにアクセスするだけで、実際に削除されることなくカウンタが減少します。それは合理的なタイミングのように聞こえるか?ノードクラスの合計サイズは約3バイトです。 – Joel