簡體   English   中英

為什么test1()的運行速度比test2()快?

[英]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編譯的因素太多:

  1. 執行統計。 在解釋器運行方法時,它收集不同的統計信息:執行了哪些路徑,采取了哪些分支,看到了哪些類實例等。在test1 ,統計信息是在第一個(空)循環內收集的,因此使JIT編譯器無法真正執行場景。
  2. 類初始化和不常見的陷阱。 test1刪除第一個System.out.println時,並非所有與打印相關的類都被初始化。 嘗試調用未初始化類的方法會導致不常見的陷阱,從而導致使用新知識對方法進行優化和進一步重新編譯。
  3. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM