2017-01-30 1 views
0

私は最近次のコードに出くわしました。ループ外でmap.end()を計算する利点

std::map<int, int> m; 

// insert into the map 

std::map<int, int>::iterator endOfMap = m.end(); 
for(std::map<int, int>::iterator itr = m.begin(); itr != endOfMap; ++itr) { 

} 

事前にendOfMapを計算する利点はありますか?

for(std::map<int, int>::iterator itr = m.begin(); itr != m.end(); ++itr) 

注:
私が見たコードは、要素の数百万人で、カスタムオブジェクトに文字列のマップでした。

+0

彼らはおそらく計算速度が非常に近いでしょうが、最初の例を取ることができます。最初の例はすでに関数呼び出しを計算し、必要なものはすべてendOfMapに渡します。終了条件の2番目の例では、m.end()関数を複数回呼び出して、等しいかどうかをチェックします。それらが等しくないかどうかをチェックするのではなく、関数を複数回呼び出さないでください。 –

+0

forループの繰り返しで、O(end())+ O(等価検査)とO(等価検査)を話しています。 –

+1

マップのデータメモリ割り当てによるパフォーマンスの低下は、ループの外側でend()を呼び出すことによる改善を上回ります! – user997112

答えて

2

mapの実装がわからないので、ループの前に割り当てを行うと、毎回ループを通してend()を呼び出すオーバーヘッドが節約されます。

しかし、そのオーバーヘッドはいくらですか? end()は暗黙的にインラインであることが既に分かっています。これはテンプレート関数なので、コードが単純であれば、コンパイラは関数呼び出しのオーバーヘッドを取り除く可能性が非常に高いです。 C++標準では、end()がO(1)関数であることが保証されています。 end()が単に他の計算なしでオブジェクトのメンバを返すのであれば、それをローカル変数にコピーするための節約はまったくないかもしれません!

一方、これはあなたに何の費用もかけない最適化の主要な例です。毎回forループでこれを行う習慣を覚えれば、それは傷つきません。私は、自分自身の行ではなく、forループの最初のセグメントで割り当てが行われたことを見てきました。

+0

ありがとうございました。下の@ Alejandroのコメントから、私はコンパイラがこれをとにかく最適化すると思いますか? –

+0

しかし、すべてのテンプレート関数はインラインですか? –

+1

@ThirupathiThangavelはい、それらはクラス宣言内で定義されているためです。テンプレート関数のすべてのコードはヘッダーファイルの一部であることに気付くでしょう。 .cppファイルにある関数があれば、代わりに基本クラスになければなりません。 –

2

事前にendOfMapを計算する利点はありますか?私はを見ることができます

唯一の利点、は、ループのすべての反復でstd::map::end()を呼び出すのコスト削減になります。その差はほとんどないだろう。あなたはそれが莫大な数の要素を持っているmapを反復することによってのみそれを見つけることができます。 std::map::end()を呼び出すコストは、標準でO(1)であると保証されていますが、何百万回も呼び出すと、関数/プログラムに顕著なコストが追加される可能性があります。

+0

@songyuanyaoそれは必ずしもO(n)一定時間ではありません。基礎となる実装は、最後の要素を常に追跡しているポインタを持つことができます。あなたがこれを把握しておらず、終わりと横断まで待つという事実を知らない限り。 –

+0

@RSahu良い、私はあなたのポイントを誤解した。 – songyuanyao

+0

@OmidCompSCI 'std :: map :: end()'は常に一定の時間を要します。これは標準規格で要求されています。 – songyuanyao

0

各繰り返しで関数呼び出しを保存しますが、map.end()が非常に軽量なメソッドであることを考慮すると、この関数を1秒間に100回呼び出すのでなければ違いは見られません。