かなりの回数のループを回すようなプログラムを書く羽目になって少し調べたので、(受け売りではあるが)まとめておく。
以下の記述は一般的なスペックを持つパソコンに合わせてあるので、組み込み等の特殊な環境には必ずしも当てはまらないことに注意。

ループ内での変数宣言

主張
ループ内で変数を宣言するとその変数の構築にコストがかかるので、ループ外で変数を宣言して使いまわした方がよい。

    for(int i=0;i<100;i++){
        int var = func();
        // varを使う処理
    }

    

    int var;
    for(int i=0;i<100;i++){
        var = func();
        // varを使う処理
    }

実際のところ

  • プリミティブ型やPOD型では速度差は出ない(最適化すれば同一のアセンブリになる)
  • PODでないクラス型ではコンストラクタの処理にかかる時間によって無視できない差が出ることも。
  • ただしPODでない型の場合、コピーコンストラクタの方がコンストラクタより重いこともある。

ループ展開

主張
ループするのにもコストがかかる。1回のループで多くの作業をしたほうが良い。

    for(int i=0;i<100;i++){
        func();
    }

    

    for(int i=0;i<10;i++){ // ループ回数は 1/10 にする
        func();
        
        func(); // 同じ処理を10回書く
    }

    

    // ループをなくす
    func();
    
    func(); // 同じ処理を100回書く

実際のところ
・中の処理が相当軽くない限り、ループのコストは大した問題にならない。
・中の処理が軽くても、100回や200回程度ではループのコストは大した差を生まない。
・完全にループ展開すればカウンタ変数の処理やジャンプ処理をすべてなくせるが、ほとんどの場合そのコストを気にする必要はない。
・これは人間のするべき仕事ではない。コンパイラにやらせるべき。
・(追記)コンパイラの最適化についてのヒントになることがあるらしい。

カウントダウンのループ

主張
コードを参照のこと。
(なぜ速くなるのかはあまりよくわからない。おそらく「0であるかどうか」は(カウンタが整数なら)「0と変数でビットORをとる」ことで行えるので、数同士の比較より速いのではないだろうか)

    for(int i=0;i<100;i++)

    

    for(int i=100;i;i--)

実際のところ
・ループのコストは余程のことがなければ気にしなくてよい。
・内部でカウンタ変数を利用する処理がある場合は書き直しが必要。処理の変更によってかえってパフォーマンスが悪くなるかも。
・読みづらいかも知れない

前置・後置インクリメント

主張
後置インクリメントは一時オブジェクトを生成するので、一時オブジェクトを生成しない前置インクリメントの方が速い。

    for(int i=0;i<100;i++)

    

    for(int i=0;i<100;++i)

実際のところ
・ループのコストは余程のことがなければ気にしなくてよい。
・使用されない一時オブジェクトは生成しないように最適化を行うコンパイラもあるらしい。