2011-12-23 6 views
2

同様の質問が、あまり具体的な: Performance issue for vector::size() in a loopパフォーマンス++メンバ関数内のループの内側のサイズ()

我々のようなメンバ関数にしていると仮定します。

void Object::DoStuff() { 

    for(int k = 0; k < (int)this->m_Array.size(); k++) 
    { 
     this->SomeNotConstFunction(); 
     this->ConstFunction(); 

     double x = SomeExternalFunction(i); 
    } 
} 

1)私は、 "SomeExternalFunction"だけがコンパイラによって最適化され、m_Arrayのsize()を重複して呼び出さない場合、それが信じられると思います。

2)あなたはほぼ確実にあなたがCONSTされていないいくつかのメンバ関数を呼び出している場合

int N = m_Array.size() 
    for(int k = 0; k < N; k++) { ... } 

をやってから速度の弾みがつくと思いませんか?

編集ない多分私は明確にすることができ、これらのダウン票とマイクロ最適化について悪意に満ちたコメントがどこから来ていることを確認:

まず、それはそれ自体は最適化することではないですが、まさにコンパイラが意志を理解します修正されません。通常はsize()関数を使用しますが、ここでは配列には何百万ものデータポイントが含まれている可能性があるため質問します。

第2に、「SomeNotConstFunction」が配列のサイズを変更する可能性が非常に低いか、またはその能力がトグルされる他の変数に依存する可能性があるという状況です。だから、私はコンパイラがどの時点でコンパイルに失敗するかを尋ねています。実際に配列がの場合はsize()によって発生する時間コストは正確には人間が知ることはできないが、の変更になります。

第三に、ループ内の操作が非常に簡単です、そこにそれらのほんの何百万人はいるが、彼らはあきれるほど平行です。私は値を外部に置くことによって、コンパイラがいくつかの作業をベクトル化できるようにすることを願っています。

答えて

9

このようなことをするのは慣れないでください。

あなたが作る最適化(2)である例:安全

  • を行うには顕著な違いがあり、あなたのコンパイラが独自の
に把握することはできません
  • 何か

    はごくわずかですが、その中間です。

    それだけで後者の二つのポイントだったら、私はちょうどあなたが重要でない何かを心配していることを助言します。しかし、最初の点は本当のキラーです:あなたはではありません。は自分に間違いを犯す機会を与えたいと思っています。早くてバグの多いコードをデバッグするよりも、ずっと遅く正しいコードを加速する方が簡単です。

    今、私はあなたの質問に答えようとします。機能SomeNotConstFunctionSomeConstFunctionの定義は(おそらく)同じ翻訳単位内にあります。したがって、これらの関数が実際にベクトルを変更しない場合、コンパイラはそれを理解することができ、sizeを1回だけ呼び出すことになります。

    ただし、コンパイラはSomeExternalFunctionの定義にアクセスできません。したがって、その関数を呼び出すたびにベクターが変更される可能性があることを前提としている必要があります。ループ内にその関数が存在することにより、「サイズは毎回」と呼ばれます。

    しかし、それはほぼ確実にインラインになるような些細な関数なので、私は "呼び出された"を引用符で囲みます。また、この機能は2つのメモリ検索(キャッシュヒットとほぼ同じ)、減算と右シフトの両方、または両方を行う特殊な単一命令であっても、お粗末です。

    SomeExternalFunctionが絶対に何もしなくても、毎回sizeを呼び出すことは、ループの実行時間のごくわずかな部分に過ぎません。

    編集:編集に対応して....

    what exactly is the time cost incurred by size() when the array really might change 
    

    あなたはタイムコードの2つの異なるバージョンをあなたが見る時間の差。そのような非常に低いレベルの最適化を行っている場合、「純粋な理由」を通して回答を得ることはできません。の結果を経験的にテストする必要があります。

    実際にこのような低レベルの最適化を行っている場合(そしてベクトルのサイズが変更されないことが保証されている場合)、おそらく配列のベースポインタがコンパイラによって認識されないということを心配する必要があります一定ではなく、サイズが一定であることを知らない。

    SomeExternalFunctionが実際にコンパイルユニットの外部にある場合は、コンパイラがループをベクトル化する機会はほとんどありません。 (リンク時には可能かもしれないと思うのですが...)また、関数呼び出しのオーバーヘッドが必要であるため、 "自明"ではありません。少なくとも、 "自明な"というのは私にとって同じことを意味します。 (また、リンク時間の最適化がどの程度良いか分かりません)

    ベクトルのサイズが変更されないことが実際に保証されている場合は、クラスのAPIを改良することを検討することもできます(少なくともprotectedまたはprivate部分)には、自明にベクトルのサイズを変更しない関数が含まれています。 vector::size()の2つの最も一般的な実装では、ループの外にそれらを巻き上げ

    return _Size; 
    

    return _End - _Begin; 
    

    であるため、マイクロ最適化の発言は、おそらくある

  • 2

    通常、サイズメソッドはコンパイラによってインライン化されるため、パフォーマンスのヒットは最小限に抑えられますが、通常は,となります。

    一方、これは通常、ベクトルに対してのみ当てはまります。たとえば、std :: listを使用している場合、sizeメソッドは非常に高価になります。

    あなたは、パフォーマンスと懸念している場合は、イテレータおよび/またはSTDのようなアルゴリズムを使用しての習慣に取得する必要があります:: for_eachではなく、サイズベースのforループを。

    +1

    C++ 11でO(1)で実行するために 'std :: list'が必要であるという' size() 'を追加したいと思います。 –

    2

    は、おそらくnoticablyパフォーマンスが改善されません。

    そして、もしそれが誰でもできることが明らかであれば、コンパイラも気づくでしょう。現代のコンパイラでは、SomeExternalFunctionが静的​​にリンクされている場合、コンパイラは、通常、呼び出しがベクトルのサイズに影響を与えるかどうかを確認できるです。

    コンパイラを信頼してください!

    +0

    質問を閉じるために投票しました。正確な答えが可能なように、正確なコードで書き直さなければなりません... –

    関連する問題