3

重複の可能性:
カウントアップよりもカウントダウンの方が速いですか?

例えば、

for (int i = 0; i < max; i++)
{
    ...
}

for (int i = max-1; i >= 0; i--)
{
    ...
}

これらの2つのループは基本的に同じであり、ループに配列操作が含まれていないことを前提としています。ただし、最初のケースでは、すべての反復でプロセッサのレジスタにmaxをロードしてから、iとmaxを比較する必要があります。一方、後者のケースでは、0がすでにレジスタにロードされている必要はありません。レジスタにあるので、後者のループの比較のみがあります。私が間違っている場合は訂正してください。正しい場合は詳しく説明してください。ありがとう。

4

4 に答える 4

6

省略記号で表されたコードは、ほぼ確実に、実際のパフォーマンスの違いを単なるノイズに追いやります。ただし、すべての仮定が正しいわけではありません。

すべての反復では、max をプロセッサのレジスタにロードしてから、i と max を比較する必要があります。

たぶん、しかしおそらくそうではありません。これはコードによって異なりますが、適切な最適化コンパイラであれば、カウンターが反復間で変化しているかどうかを検出できます。

どこからアイデアを得たのかわかりませんが、それらは少し見当違いであり、最適化コンパイラがどのように機能するかを考慮していません。あなたの分解を見て、本当の違いがあなた自身であることを見てください. なんてこった、やります(とにかく楽しいです):

プログラムは次のとおりです。

int main(int argc, char *argv[]){   
    int max = 10;
    for (int i = max-1; i >= 0; i--)
    {
        cout << i;
    }
    return 0;
}

生成されたアセンブリ (VS2010 リリース、私自身のコメント) は次のとおりです。

int main(int argc, char *argv[]){   
00341000  push        esi  
    int max = 10;
    for (int i = max-1; i >= 0; i--)
00341001  mov         esi,9               ; move a static 9 into esi
00341006  jmp         main+10h (341010h)  
00341008  lea         esp,[esp]           ; load the address of whatever
0034100F  nop                             ; esp points to in memory 
    {                                     ; (not a memory fetch, just address calculation)
        cout << i;
00341010  mov         ecx,dword ptr [__imp_std::cout (342048h)]  
00341016  push        esi  
00341017  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (342044h)]  
0034101D  dec         esi                 ; decrement counter
0034101E  jns         main+10h (341010h)  ; jump if not signed
    }

そして、より慣用的なバージョンについては...

int main(int argc, char *argv[]){   
00AC1000  push        esi  
    int max = 10;
    for (int i = 0; i < max; i++)
00AC1001  xor         esi,esi  
    {
        cout << i;
00AC1003  mov         ecx,dword ptr [__imp_std::cout (0AC2048h)]  
00AC1009  push        esi  
00AC100A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0AC2044h)]  
00AC1010  inc         esi               ; increment esi
00AC1011  cmp         esi,0Ah           ; compare to 10 (0Ah)
00AC1014  jl          main+3 (0AC1003h) ; if less, jump to top 
    }

そうです、最初のバージョンはjns命令 (署名されていない場合はジャンプ) を使用するため、比較は少し単純化されています (0 と比較して)。さらにいくつかの指示が含まれていますが、比較はありません。

ただし、バージョン 2 で行われた比較も静的であることに注意してください。maxループ全体で変化しないことがわかっているため、それに応じてそのビットを最適化できます。

ただし、これによってパフォーマンスが大幅に向上する可能性は低いことを強く繰り返します。私の Windows PC の高性能タイマーでさえ、2 つの間の良好な統計的差異を与えることができませんでしたcout

于 2012-07-18T21:09:16.987 に答える
2

コンパイラ レベルの最適化は、ループの内容に応じて十分に変化するため、これはおそらく議論の余地があるポイントです。たとえば、コンパイラは Max の値がループ中に変更されないことを認識できる場合があるため、一度だけメモリにロードします。

このレベルのマイクロ最適化について本当に心配している場合は、使用する予定のコンパイラとコンパイラの設定を正確に把握し、ターゲット ハードウェアでタイミング テストを行って、さまざまなオプションを比較する必要があります。または、コンパイラの出力を直接見て、実際のアセンブリまたはマシンレベルの命令を比較して、あるバージョンが別のバージョンよりも多くの命令を使用しているかどうかを確認できます。

于 2012-07-18T21:00:25.417 に答える
1

増加または減少の選択は、通常、パフォーマンスに関連するものには基づいていません。これは通常、ループのコンテキストに最も適したアルゴリズムまたはコードのビットの論理フローに基づいています。

実装によっては、プリインクリメント (++i) がポストインクリメント (i++) よりも高速になることがありますが、コンパイラーはほとんどのループを最適化したり、反復回数が一定の場合は完全にアンロールしたりします。通常、テストでパフォーマンスのボトルネックを特定するまで、コードを最適化する価値はありません。

要するに、小さなことを気にしないでください。

于 2012-07-18T21:03:52.523 に答える
0

はい、0 との比較は非ゼロとの比較よりも優れているため、より良い可能性があります。しかし、最新のコンパイラは通常、コードの最適化において適切に機能しているため、大きな違いはありません。

最後のポイント - これはマイクロ最適化です。コードが読みやすくならない限り、私はそれを避けます。

于 2012-07-18T21:04:12.683 に答える