簡體   English   中英

困惑於java if else表現

[英]puzzled with java if else performance

我正在對方法的性能進行調查,最后發現開銷是由if else語句的“else”部分引起的。 我編寫了一個小程序來說明性能差異,即使代碼的else部分永遠不會被執行:

public class TestIfPerf
{
    public static void main( String[] args )
    {   
        boolean condition = true; 
        long time = 0L;
        int value = 0;
        // warm up test 
        for( int count=0; count<10000000; count++ )
        {       
            if ( condition ) 
            {           
                value = 1 + 2;  
            }           
            else        
            {           
                value = 1 + 3;  
            }           
        }       
        // benchmark if condition only
        time = System.nanoTime();
        for( int count=0; count<10000000; count++ )
        {
            if ( condition )
            {
                value = 1 + 2;
            }           
        }
        time = System.nanoTime() - time; 
        System.out.println( "1) performance " + time ); 
        time = System.nanoTime();
        // benchmark if else condition
        for( int count=0; count<10000000; count++ )
        {
            if ( condition )
            {
                value = 1 + 2;
            }           
            else
            {
                value = 1 + 3;
            }
        }
        time = System.nanoTime() - time; 
        System.out.println( "2) performance " + time ); 
    }   
}

並使用java -classpath . -Dmx=800m -Dms=800m TestIfPerf運行測試程序java -classpath . -Dmx=800m -Dms=800m TestIfPerf java -classpath . -Dmx=800m -Dms=800m TestIfPerf

我在Mac和Linux Java上使用1.6最新版本執行此操作。 一直是沒有else的第一個基准測試比使用else部分的第二個基准測試快得多,即使代碼的結構使得else部分由於條件而永遠不會被執行。 據我所知,差異可能不大,但相對性能差異很大。 我想知道是否有人對此有任何見解(或者可能是我做錯了)。


Linux基准測試(納米級)

  1. 表現1215488
  2. 表現2629531

Mac基准測試(納米級)

  1. 表現1667000
  2. 表現4208000

你的考試是鋪位。 如果我交換測試條件,我會得到完全相反的結果:

1) performance 5891113
2) performance 15216601

2) performance 5428062
1) performance 15087676

它可能與JVM在執行過程中優化代碼有關。 如果我復制/粘貼條件幾次,我得到這個:

2) performance 6258694
1) performance 34484277
2) performance 978
1) performance 978
2) performance 908

有兩種可能的解釋:

  • 你得到的時間被基准缺陷扭曲了。 你做了很多錯事 - 請參閱如何在Java中編寫正確的微基准測試?

  • 真正使用else的版本每次循環迭代需要稍長的時間。 如果是這種情況,則有許多可能的解釋。 掌握它的最佳方法是查看JIT編譯器生成的本機代碼並分析其性能。

但最重要的是,這並不令人驚訝(見上文),或絕大多數Java應用程序的任何實際后果。 應用程序確定是否需要“if then”或“if then else”。

您可能從這樣的人工微基准測試中學到的任何東西都會對實際代碼有所啟發,這是值得懷疑的。 JIT編譯器可能會以比測試運行更復雜的級別優化代碼。 你在這里看到的(如果你的基准沒有缺陷)不太可能反映在真實的應用程序中。

如果你在第二個測量中注釋掉else分支,它會變得更奇怪:它仍然更慢,盡管它現在是相同的代碼。 好消息是,如果您使用單獨的方法提取此代碼,則第二次運行速度會快得多。

我唯一能想到的是JVM只優化了長方法的第一部分。 是的:如果我把if-else測量測量放在第一位,那就更快了。

必須與VM init(預熱很短)或時間測量中的抖動(與VM啟動相關)有關。

如果你交換循環,循環2變得更快:-)

一般來說,熱點JIT是體面但不可靠的,而不是確定性的。 在java中獲得最佳性能

  • 避免創建對象
  • 盡可能將旗幟作為決賽。 有時這並沒有什么不同,但有時它確實有所不同。 在循環中訪問實例變量時,將它們復制到最終本地。
  • 如果可能,標記方法最終。
  • 如果你想要一個方法來內聯,保持簡短,可能會分手。 我通過測試多次驗證,通過拆分“void foo(){if(..)return ..stuff ...}”得到內聯/更快分割時如“foo(){if(..)return; else foo1()} void foo1(){stuff}“

一般來說,使用微基准來證明性能模式非常困難,因為您不知道究竟是什么觸發了內聯,jit編譯和進一步的運行時優化。 JIT中存在閾值,因此可能會導致性能降低,這只是因為您向方法添加了語句或添加了現有類的子類。

Java代碼:

        for (int count = 0; count < 10000000; count++) {
            // start of the if
            if (cond) {
                value = 1 + 2;
            }
            // end of the if
        }

Java字節碼:

   7:   lstore_3      //store a long value in a local variable 3
   8:   iconst_0      //load the int value 0 onto the stack
   9:   istore  5     //store int value into variable #index
   11:  goto    23    //goes to another instruction at branchoffset
   14:  iload_1       //load an int value from local variable 1
   15:  ifeq    20    //if value is 0, branch to instruction at branchoffset
   18:  iconst_3      //load the int value 3 onto the stack
   19:  istore_2      //store int value into variable 2
   20:  iinc    5, 1  //increment local variable #index by signed byte const
   23:  iload   5     //load an int value from a local variable #index
   25:  ldc #22;  //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
   27:  if_icmplt 14  //if value1 is less than value2, branch to instruction at branchoffset

Java代碼:

        for (int count = 0; count < 10000000; count++) {
            // start of the if
            if (cond) {
                value = 1 + 2;
            } else {
                value = 1 + 3;
            }
            // end of the if
        }

Java字節碼:

   63:  lstore_3      //store a long value in a local variable 3
   64:  iconst_0      //load the int value 0 onto the stack
   65:  istore  5     //store int value into variable #index
   67:  goto    84    //goes to another instruction at branchoffset
   70:  iload_1       //load an int value from local variable 1
   71:  ifeq    79    //if value is 0, branch to instruction at branchoffset
   74:  iconst_3      //load the int value 3 onto the stack
   75:  istore_2      //store int value into variable 2
   76:  goto    81    //goes to another instruction at branchoffset
   79:  iconst_4      //load the int value 4 onto the stack
   80:  istore_2      //store int value into variable 2
   81:  iinc    5, 1  //increment local variable #index by signed byte const
   84:  iload   5     //load an int value from a local variable #index
   86:  ldc #22;  //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
   88:  if_icmplt 70  //if value1 is less than value2, branch to instruction at branchoffset
public class Main {

public static void main(String[] args) {
    boolean cond = true;
    int nothing = 0;

    for (int i = 0; i < 20; i++) {
        int value = 0;
        long time = System.nanoTime();
        for (int count = 0; count < 10000000; count++) {
            if (cond) {
                value = 1 + 2;
            }
        }

        time = System.nanoTime() - time;
        System.out.println("1) performance: " + time);
        nothing = value; // prevent java ignoring value

        value = 0;
        time = System.nanoTime();
        for (int count = 0; count < 10000000; count++) {
            if (cond) {
                value = 1 + 2;
            } else {
                value = 1 + 3;
            }
        }
        time = System.nanoTime() - time;
        System.out.println("2) performance: " + time);
        nothing = value; // prevent java ignoring value
    }

    nothing = nothing + 1;
}
}

這是結果:

1) performance: 1797000
2) performance: 3742000
1) performance: 7290000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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