1

非常に具体的な if 条件を評価しているときに、非常に奇妙なパフォーマンス動作が発生しています。

基本的に、Java が条件の遅延評価を行うべきであるという事実と直接衝突するように見える場合でも、常に false の場合if(a)よりも 10 倍高速な 2 つの条件 a と b を作成できることがわかりました。if(a && b)a

このケースは、私が存在しなけれaばならない場合someDate.after(startdate)に発生bしますsomeDate.before(enddate)-つまり、かなり標準的な範囲の状態です。

以下に、この特定の問題を示すために使用するコードを添付しました。私は Java 7 を使用して Windows 7 でコードを実行し、同僚の 1 人が Java 6、7、および 8 を使用して Windows 7 でコードを実行しましたが、結果はすべて同じでした。

なぜこれが起こるのか、誰か説明できますか?

import java.util.Date;

public class DateWeirdness {
public static void main(String[] args) {
    Date start = new Date();
    System.out.println("if(a) - a always false");
    System.out.println(timeBasic(2000000000, start, start.getTime() - 4000000000L));
    start = new Date();
    System.out.println("if(a && b) - a always false, a and b both date method");
    System.out.println(timeAdv(2000000000, start, start.getTime() - 4000000000L, new Date()));
    start = new Date();
    System.out.println("if(a && b) - a always false, b is condition on longs not date");
    System.out.println(timeAdv2(2000000000, start, start.getTime() - 4000000000L, new Date().getTime()));
    start = new Date();
    System.out.println("if(a) - a always false - condition on long");
    System.out.println(timeBasicL(2000000000, start.getTime(), start.getTime() - 4000000000L));
    start = new Date();
    System.out.println("if(a && b) - a always false, a and and b both conditions on long");
    System.out.println(timeAdvL(2000000000, start.getTime(), start.getTime() - 4000000000L, new Date().getTime()));
    start = new Date();
    System.out.println("if(a && b) - a always false, b always true");
    System.out.println(timeAdv(2000000000, start, start.getTime() - 4000000000L, new Date()));
    start = new Date();
    System.out.println("if(a && b) - both true");
    System.out.println(timeAdv(2000000000, start, start.getTime() + 1, new Date(start.getTime() + 4000000000L)));
    start = new Date();
    System.out.println("if(a && b) - a always true, b always false");
    System.out.println(timeAdv(2000000000, start, new Date(start.getTime() + 4000000001L).getTime(), new Date(start.getTime() + 4000000000L)));
}

private static int timeBasic(int size, Date start, long l) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date)) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdv(int size, Date start, long l, Date end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date) && end.after(date)) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdv2(int size, Date start, long l, long end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date) && end > l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeBasicL(int size, long start, long l) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        l++;
        if (start < l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdvL(int size, long start, long l, long end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        l++;
        if (start < l && end > l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}
}

ローカルで実行すると、次の出力が得られます。興味深いテストは、最初の 3 つのテストです。640は実行のミリ秒単位のパフォーマンスif(a)7079は実行のパフォーマンスif(a && b)、ここでabは上記のとおりです。は がどこにあるか710のパフォーマンスです。if(a && b)bsomeDateAsLong < endDateAsLong

if(a) - a always false
640
0
if(a && b) - a always false, a and b both date method
7079
0
if(a && b) - a always false, b is condition on longs not date
710
0
if(a) - a always false - condition on long
639
0
if(a && b) - a always false, a and and b both conditions on long
708
0
if(a && b) - a always false, b always true
6873
0
if(a && b) - both true
11995
2000000000
if(a && b) - a always true, b always false
13746
0
4

2 に答える 2

2

何の役にも立たないコードを実行する場合、JIT が何を排除できるかどうかに依存します。あなたの場合、コードが削除されているようです。

高速の場合、各ループは 0.32 ナノ秒かかりますが、これは短すぎて現実的ではありません。つまり、約 1 クロック サイクルです。より遅いケースは 3.6 ns で、より現実的ですが、ほとんど最適化されていないことを意味する可能性があります。

動作を常に false から常に true に、またはその逆に切り替える場合、コードを再最適化する必要があり、実際の実行にかかる時間ではなく、これを検出してコードを置き換えるのにかかる時間を計測する場合があります。

main() 内のすべてのコードを 3 回実行する外側のループを追加することをお勧めします。そうすれば、さらに劇的な違いが見られるはずです。

于 2013-10-14T11:12:25.210 に答える
0

start.before(date)JVM は、最初のケースではまったく評価されない方法でコードを最適化します(私はそう思います)。で実行してみて-Djava.compiler=NONE、結果を確認してください。

于 2013-10-14T11:10:24.987 に答える