2017-02-21 13 views
4

thisによると、あなたはstd::mapのためのスペースを確保することはできません。なぜstd :: unordered_mapに予約メソッドがありますか?

いいえ、マップのメンバーは、内部ツリー構造に格納されています。 格納されるキーと値 がわかるまで、ツリーを構築する方法はありません。このことから

std::mapはそれがcppreference.comにしreserve()方法を、不足する理由は明白です。しかし、std::unordered_mapないreserve()方法がありますが、私はoperator[]insert()またはemplace()でそれを使用しようとすると、彼らはすべてのreserve()最初に呼び出さた私にもかかわらず、メモリを割り当てるために行きます。

これは何ですか? reserve()が必要なスペースを適切に予約していないのはなぜですか?マップと同じように事前にメモリを割り当てることができない場合は、std::unordered_mapはなぜ最初にreserve()メソッドを持っていますか?

+0

演算子[]、insert()またはemplace()が.reserve()以外のメモリを使用する代わりにメモリを割り当てることを決定するために使用した方法を省略できますか? – nos

+0

@nos私はすべての呼び出しでアセンブリを踏んで、最終的に 'operator new()'と 'malloc()'と呼ばれる何らかの 'List_buy()'または 'BuyNode()'呼び出しで終了しました。 – Mikubyte

答えて

8

unordered_mapコンテナは、mapのようなツリーではなく、バケットを使用して実装されているため、reserveメソッドがあります。

バケットがある:

要素は、そのキーのハッシュ値に基づいて割り当てられているコンテナの内部ハッシュテーブルのスロット。バケットには0から(bucket_count-1)までの番号が付けられます。 (source

単一のバケットは、可変数の項目を保持します。この数値はload_factorに基づいています。 load_factorが一定のしきい値に達すると、コンテナはバケットの数を増やしてマップを再ハッシュします。

reserve(n)を呼び出すと、コンテナは少なくともnのアイテムを保持するのに十分なバケットを作成します。

これは、nに直接バケット数を設定し、ハッシュテーブル全体の再構築をトリガーするrehash(n)とは対照的です。

も参照してください:Pre-allocating buckets in a C++ unordered_map

編集コメント

に応じて、私はコメントで提起質問に対する正確な答えを知らない、と私の予備調査として実りを証明していないとして、私は実験的にテストすることにしました。参考のため

、質問がに沸く:

n個の要素のためのバケットを予約すると、n個の要素のためにメモリを割り当てると同じである場合は、説明していただけますか?

this answerによると、正確unordered_mapに割り当てられた領域のサイズを取得することは難しいと信頼できないです。だから私はVisual Studio 2015の診断ツールを利用することに決めました。次のように

まず、私のテストケースは次のとおりです。

#include <unordered_map> 
#include <cstdint> 

struct Foo 
{ 
    Foo() : x(0.0f), y(0.0f), z(0.0f) { } 

    float x; 
    float y; 
    float z; 
}; 

int32_t main(int32_t argc, char** argv) 
{ 
    std::unordered_map<uint32_t, Foo> mapNoReserve; 
    std::unordered_map<uint32_t, Foo> mapReserve; 

    // --> Snapshot A 

    mapReserve.reserve(1000); 

    // --> Snapshot B 

    for(uint32_t i = 0; i < 1000; ++i) 
    { 
     mapNoReserve.insert(std::make_pair(i, Foo())); 
     mapReserve.insert(std::make_pair(i, Foo())); 
    } 

    // -> Snapshot C 

    return 0; 
} 

コメントが示す

が、私はメモリのスナップショットを取りました。

結果は以下の通りであった:

スナップショットA:

┌──────────────┬──────────────┬──────────────┐ 
|  Map  | Size (Bytes) | Bucket Count | 
|--------------|--------------|--------------| 
| mapNoReserve | 64   | 8   | 
| mapReserve | 64   | 8   | 
└──────────────┴──────────────┴──────────────┚ 

スナップショットB:

┌──────────────┬──────────────┬──────────────┐ 
|  Map  | Size (Bytes) | Bucket Count | 
|--------------|--------------|--------------| 
| mapNoReserve | 64   | 8   | 
| mapReserve | 8231   | 1024   | 
└──────────────┴──────────────┴──────────────┚ 

スナップショットC:

┌──────────────┬──────────────┬──────────────┐ 
|  Map  | Size (Bytes) | Bucket Count | 
|--------------|--------------|--------------| 
| mapNoReserve | 24024  | 1024   | 
| mapReserve | 24024  | 1024   | 
└──────────────┴──────────────┴──────────────┚ 

解釈:あなたがスナップショットから見ることができるように

、我々が、彼らにreserve呼ばれていたとしても1に要素を追加し始めると、両方のマップのサイズが大きくなることが表示されます。

reserveはまだメモリが割り当てられていても利点がありますか? (1)バケットのメモリをあらかじめ割り当てておき、(2)rehashの必要性を防ぐことができます。これは前述のように完全にマップを再構築します。

+0

リンクされた質問では、 'rehash()'と 'reserve()'の両方がバケットをあらかじめ割り当てていると言われていますが、 'reserve(pow(2、x))' 、x)は挿入しようとしている要素の数です**。どういう意味ですか? 'reserve()'が引数として100万を与えるとXバケットが必要であることを知っていることを意味しますか?それにもかかわらず、必要なバケットを予約しておけば、なぜメモリを割り当てる必要がありますか?予約は最適化されているので、後で新しいバケットを作成する必要はありませんが、実際の要素にはまだ割り当てられたメモリが必要ですか? – Mikubyte

+0

はい、 'reserve(n)'を呼び出すと、少なくとも 'n '個のアイテムを保持するのに十分なバケットが作成されます。 '> n'個のアイテムをマップに追加すると、負荷係数に応じて再ハッシュがトリガーされることがあります。 – ssell

+0

これは割り当てられたメモリとどう違うのかまだ分かりませんが、残念です。 'n'要素のバケットの予約が' n'要素のためのメモリの割り当てと同じであるかどうかを説明してください。私が予約しているにもかかわらず挿入すると、それは明らかに 'new()'を内部的に呼び出すからです。 – Mikubyte

関連する問題