2010-03-11 31 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内联,如果不是,那么可变参数的开销可能会忽略不计。