13

プリミティブ配列の for ループ間にパフォーマンスの違いはありますか?

推定:

double[] doubleArray = new double[300000];


for (double var: doubleArray) 
   someComplexCalculation(var);

また :

for ( int i = 0, y = doubleArray.length; i < y; i++)
   someComplexCalculation(doubleArray[i]);

テスト結果

私は実際にそれをプロファイリングしました:

Total timeused for modern loop= 13269ms
Total timeused for old loop   = 15370ms

したがって、少なくとも私の Mac OSX JVM 1.5 では、最新のループの方が実際には高速に実行されます。

4

5 に答える 5

5

私の意見では、あなたは知らないので、推測するべきではありません。最近、コンパイラをアウトスマートにしようとしても無駄です。

ある操作を最適化するように見える「パターン」を人々が学んだことがありますが、Javaの次のバージョンでは、それらのパターンは実際には遅くなりました。

可能な限り明確に記述し、実際にユーザー仕様を手に入れて要件を満たせなくなるまで最適化について心配する必要はありません。それでも、テストの前後に慎重に実行して、 「修正」は、実際には、その要件を通過させるのに十分なほど改善されました。

コンパイラーは、靴下を吹き飛ばすような驚くべきことを実行できます。広い範囲で繰り返すテストを行ったとしても、範囲が狭い場合やループ内で発生することを変更した場合は、まったく異なるパフォーマンスになる可能性があります。

ジャストインタイムコンパイルとは、Cを上回ることがあることを意味し、場合によっては静的アセンブリ言語を上回ることができない理由はありません(アセンブリは、呼び出しが不要であることを事前に判断できません。Javaはそれを実行できる場合があります。

要約すると、コードに入れることができる最大の価値は、読み取り可能になるように記述することです。

于 2008-11-05T21:05:34.223 に答える
4

手書きの「古い」フォームは、実行する命令が少なく、高速である可能性がありますが、確実に知るには、特定のJITコンパイラーでプロファイルを作成する必要があります。「新しい」フォームは間違いなく高速ではありません。

分解されたコード(SunのJDK 1.5によってコンパイルされたもの)を見ると、「新しい」形式が次のコードと同等であることがわかります。

1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3:   double var = tmp[i];
4:   someComplexCalculation(var);
5: }

したがって、より多くのローカル変数が使用されていることがわかります。doubleArray1行目のへの割り当てtmpは「余分」ですが、ループ内では発生せず、おそらく測定できません。3行目のへの割り当てvarも追加です。パフォーマンスに違いがある場合は、これが原因です。

1行目は不要に思えるかもしれませんが、ループに入る前に配列がメソッドによって計算される場合は、結果をキャッシュするのが定型です。

そうは言っても、インデックス変数で何かをする必要がない限り、私は新しいフォームを使用します。パフォーマンスの違いは、実行時にJITコンパイラーによって最適化される可能性が高く、新しい形式はより明確になります。「手作業で」それを続けた場合、将来の最適化を見逃す可能性があります。一般に、優れたコンパイラは「愚かな」コードをうまく最適化できますが、「賢い」コードにつまずきます。

于 2008-11-05T21:17:25.620 に答える
2

自分で測ってみませんか?

これは少し厳しいように聞こえますが、この種の質問は自分自身を確認するのが非常に簡単です.

配列を作成して各ループを 1000 回以上実行し、時間を測定するだけです。数回繰り返して、不具合を解消します。

于 2008-11-05T20:39:07.383 に答える
2

違いはありません。Java は拡張 for を通常の for ループに変換します。強化された for は単なる「構文糖衣」です。生成されるバイトコードは、両方のループで同じです。

于 2008-11-05T20:45:17.370 に答える
1

以前の回答の後でも、あなたの質問に非常に興味がありました。だから私も自分でチェックすることにしました。私はこの小さなコードを書きました(数が素数であるかどうかをチェックすることについての数学の正しさを無視してください;-)):

public class TestEnhancedFor {

    public static void main(String args[]){
        new TestEnhancedFor();
    }

    public TestEnhancedFor(){
        int numberOfItems = 100000;
        double[] items = getArrayOfItems(numberOfItems);
        int repetitions = 0;
        long start, end;

        do {
            start = System.currentTimeMillis();
            doNormalFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Normal For. Repetition %d: %d\n", 
                    repetitions, end-start);

            start = System.currentTimeMillis();
            doEnhancedFor(items);
            end = System.currentTimeMillis();
            System.out.printf("Enhanced For. Repetition %d: %d\n\n", 
                    repetitions, end-start);

        } while (++repetitions < 5);
    }

    private double[] getArrayOfItems(int numberOfItems){
        double[] items = new double[numberOfItems];
        for (int i=0; i < numberOfItems; i++)
            items[i] = i;
        return items;
    }

    private void doSomeComplexCalculation(double item){
        // check if item is prime number
        for (int i = 3; i < item / 2; i+=2){
            if ((item / i) == (int) (item / i)) break;
        }
    }

    private void doNormalFor(double[] items){
        for (int i = 0; i < items.length; i++)
            doSomeComplexCalculation(items[i]);
    }

    private void doEnhancedFor(double[] items){
        for (double item : items)
            doSomeComplexCalculation(item);
    }

}

アプリを実行すると、次の結果が得られました。

通常の場合。繰り返し0:5594拡張。繰り返し0:5594

通常の場合。繰り返し1:5531拡張。繰り返し1:5547

通常の場合。繰り返し2:5532拡張。繰り返し2:5578

通常の場合。繰り返し3:5531拡張。繰り返し3:5531

通常の場合。繰り返し4:5547拡張。繰り返し4:5532

ご覧のとおり、結果間のばらつきは非常に小さく、通常のループの実行速度が速い場合もあれば、拡張ループの速度が速い場合もあります。私のコンピューターでは他のアプリが開いているので、正常だと思います。また、最初の実行だけが他の実行よりも遅くなります。これはJITの最適化に関係していると思います。

平均時間(最初の繰り返しを除く)は、通常のループでは5535,25ミリ秒、拡張ループでは5547ミリ秒です。しかし、両方のループの最適な実行時間は同じ(5531ms)であることがわかります。したがって、両方のループのパフォーマンスは同じであり、経過時間の変動は他のアプリケーションによるものであるという結論に達することができると思います(マシンのOS)。

于 2008-11-06T16:20:37.357 に答える