[英]Why does test1() run much faster than test2()?
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編譯。 而且,JIT足夠聰明,可以意識到您的代碼除了獲得當前時間和打印時間差之外,沒有做任何有用的事情。 因此,它通過根本不運行來優化無用的代碼。
如果您編寫實際的,有用的代碼,則JIT將做正確的事情。 不要試圖通過添加空循環來弄亂它。
影響JIT編譯的因素太多:
test1
,統計信息是在第一個(空)循環內收集的,因此使JIT編譯器無法真正執行場景。 test1
刪除第一個System.out.println
時,並非所有與打印相關的類都被初始化。 嘗試調用未初始化類的方法會導致不常見的陷阱,從而導致使用新知識對方法進行優化和進一步重新編譯。 r==0?1:0
替換為a()
時,在test1
中收集的錯誤統計信息會成為一個惡作劇。 在已編譯的test1
,方法a()
從未執行過,因此沒有機會進行優化。 這就是為什么它比test2
慢的原因,后者已經在a()
的知識下進行a()
編譯。 當然,在嘗試從頭開始編寫微基准測試時,很難預測影響JIT編譯的所有因素。 這就是為什么推薦的對代碼進行基准測試的方法是使用特殊的框架,在這些框架中大多數問題已經得到解決。 我個人建議JMH 。
優化代碼后,它將使用有關優化之前代碼運行方式的信息。 在test1()中,第一個循環觸發整個方法的優化,但是沒有關於第二個循環將如何運行的信息,因此它沒有像test2()那樣被優化
我期望應該發生的是重新優化了該方法,但是代碼必須檢測到它第一次進行的假設是無效的。
猜測可能有什么不同,test2()可能已經展開循環,而在test1()中則沒有。 這可以解釋性能上的差異。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.