import java.util.Random;
public class Test{
static int r = new Random().nextInt(2);
static int a(){
return r==1 ? 1 :0;
}
public static void test1() throws Exception {
//
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
// Thread.sleep(20000);
long d = 0;
for (int j = 0; j < 10; j++) {
long y = System.currentTimeMillis();
for (int x = 0; x < 1073741823; x++) {
d += r==0?1:0;
}
System.out.println((System.currentTimeMillis() -y));
}
}
public static void test2() throws Exception{
// Thread.sleep(20000);
long d = 0;
for (int j = 0; j < 10; j++) {
long y = System.currentTimeMillis();
for (int x = 0; x < 1073741824; x++) {
d += r==0?1:0;
}
System.out.println((System.currentTimeMillis() -y));
// System.out.println("time:"+ (System.currentTimeMillis() - y));
}
}
public static void main(String[] args) throws Exception{
// Thread.sleep(20000);
test1();
test2();
}
}
上記のコードを実行すると、次の出力が得られます。
32
26
28
28
32
29
35
33
30
31
1321
1308
1324
1277
1348
1321
1337
1413
1287
1331
test1がはるかに高速なのはなぜですか?
以下を除いて違いはありません。
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
また、test1の時間コストは25〜35ミリ秒で、信じられないほどです。同じコードをCで記述しましたが、すべてのforループを実行するのに約4秒かかりました。
この振る舞いは奇妙に思えます。追加するタイミングを知るにはどうすればよいですか。
System.out.println(1403187139018L);
for (int i = 0; i < 1073741824; i++) {}//*
また、私が変更した場合
r==0?1:0
に
a()
その場合、test2()はtest1()よりも高速に実行されます。
私が得る出力は次のとおりです。
1403187139018
3726
3729
3619
3602
3797
4362
4498
3816
4143
4368
1673
1386
1388
1323
1296
1337
1294
1283
1235
1460
元のレガシーコード:..。
long t = System.currentTimeMillis();
MappedByteBuffer mbb = map(new File("temp.mmp"), 1024L * 1024 * 1024);
System.out.println("load " + (System.currentTimeMillis() - t));//*
for (int i = 0; i < 2014L * 1024 * 1024; i++) {}//*
int d = 0;
for (int j = 0; j < 10; j++) {
t = System.currentTimeMillis();
mbb.position(0);
mbb.limit(mbb.capacity());
for (int i = 0; i < mbb.capacity(); i++) {
d += mbb.get();
}
....
}
System.out.println(d);
JITコンパイルに影響を与える要因が多すぎます。
test1
統計が収集されます。統計では、最初の(空の)ループ内で収集されるため、実際の実行シナリオについてJITコンパイラがだまされます。 。System.out.println
から最初のものを削除すると、test1
印刷に関連するすべてのクラスが初期化されるわけではありません。初期化されていないクラスのメソッドを呼び出そうとすると、珍しいトラップが発生し、最適化が解除され、新しい知識を使用してメソッドがさらに再コンパイルされます。test1
置き換えるr==0?1:0
と悪い冗談を言いますa()
。コンパイルでtest1
は、メソッドa()
はこれまで実行されたことがないため、最適化する機会がありませんでした。そのためtest2
、の知識を使用してコンパイルされたものよりも動作が遅くなりますa()
。もちろん、マイクロベンチマークを最初から書き込もうとすると、JITコンパイルに影響を与えるすべての要因を予測することは困難です。そのため、コードのベンチマークを行うための推奨される方法は、これらの問題のほとんどがすでに解決されている特別なフレームワークを使用することです。私は個人的にJMHを提案します。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加