2010-03-11 64 views
12

編碼我到處檢查Java的可變參數性能。Java的可變參數性能

我寫下面的測試代碼:

public class T { 

    public static void main(String[] args) { 

     int n = 100000000; 
     String s1 = new String(""); 
     String s2 = new String(""); 
     String s3 = new String(""); 
     String s4 = new String(""); 
     String s5 = new String(""); 

     long t = System.currentTimeMillis(); 
     for (int i = 0; i < n; i++) { 
      foo(); 
     } 
     System.err.println(System.currentTimeMillis() - t); 


     t = System.currentTimeMillis(); 
     for (int i = 0; i < n; i++) { 
      baz(s1, s2, s3, s4, s5); 
     } 
     System.err.println(System.currentTimeMillis() - t); 

     t = System.currentTimeMillis(); 
     for (int i = 0; i < n; i++) { 
      bar(s1, s2, s3, s4, s5); 
     } 
     System.err.println(System.currentTimeMillis() - t); 

    } 

    static void foo() { 
    } 

    static void bar(String a1, String a2, String a3, String a4, String a5) { 
    } 

    static void baz(String... a) { 
    } 
} 

在我的機器的平均產量是:

78 
4696 
78 

似乎傳遞變量的方法是不花錢?!很好!

但是使用varags的速度要慢60倍!爲什麼?

解釋可能是程序必須在堆上創建數組,並且時間是由GC支出的。但是對於少環路我仍然得到作爲輸出:

0 
62 
0 

什麼是花這個額外的時間,反正編譯器的所有信息來解決這一個修復可變調用...

它不是我的本意以優化,但我發現這個奇怪...

更新

我添加了一個新的測試

t = System.currentTimeMillis(); 
for (int i = 0; i < n; i++) { 
    baz(s1); 
} 
System.err.println(System.currentTimeMillis() - t); 

而這一個參數版本仍然慢30倍。也許在場景後面有一個ArrayList.toArray()?

因此,請注意代碼中的不需要的varags方法和重構來修復長度。這可能是性能提升。

回答

16

參數的靜態列表與數組完全不同。當你以這種方式傳遞它們時,編譯器爲引用保留空間並在方法被調用時填充它們。

可變參數是一個等價的數組。要調用這種方法,有必要在運行時創建並填充數組。這就是你觀察其差異的原因。

String[]String...是同義詞。如果你比較它們,你應該看到相同的性能。

+0

是的,它將是相同的,因爲可變參數是在有效編譯之前將語法糖轉換爲數組調用。 – Riduidel 2010-03-11 16:00:59

+0

這基本上是正確的,雖然我認爲你應該澄清,差異是可變參數需要JVM分配和填充數組。不管它堆在堆棧上還是堆棧都不是問題(雖然當然是堆在堆上)。 – 2010-03-12 09:47:53

+0

@肖恩歐文謝謝,更新。 – 2010-03-12 09:59:21

1

有趣的問題!

這只是一個猜測:在幕後,Var-args只是數組。創建這個隱式數組並使用var-args參數填充它可能需要一些時間;因此性能受到影響。好吧,我猜。

0

至於說,一個陣列使用VAR-ARGS ...當保持,

你也應該嘗試看看加入「最終」的每一個方法的參數的影響

我親自動手陣列的2250 - > 2234毫秒的改進。

+0

Java中的參數確實無法做到最終,它們仍然可以改變,所以這不應該影響任何東西。 – helpermethod 2010-03-11 17:00:12

+0

如何更改最終參數? – Hardcoded 2010-03-12 08:33:24

+0

最終參數和變量不能更改。我認爲這樣說更好一點:做一個*對象引用*最後意味着你不能改變引用,但並不意味着它引用的對象不能改變狀態。 – 2010-03-12 09:46:56

7

同時使用最新的JRE6和JRE7我得到的比你不同的結果,它們表明,可變參數是快5倍:

69 
69 
311 

不過,我不敢妄下結論,因爲這個測試有幾個缺陷:在參數不在函數中使用;該功能不起任何作用;參數具有相同的值。 JIT可以輕鬆優化此代碼和內聯函數調用。我修改你的榜樣,以解決上述存在的突出問題,並得到了以下結果:

627 
7470 
7844 

得出的結論是:不要猶豫,使用可變參數。如果你的函數是微不足道的,那麼它的調用由JIT內聯,如果不是,那麼可變參數的開銷可能會忽略不計。