Java varargs性能

时间:2010-03-11 15:54:48

标签: java performance

编码我来检查Java的vararg性能。

我写下以下测试代码:

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方法,并重构以修复长度。这可能是一种性能提升。

5 个答案:

答案 0 :(得分:18)

参数的静态列表与数组完全不同。当您以这种方式传递它们时,编译器会为引用保留空间,并在调用方法时填充它们。

Varargs相当于数组。要调用此类方法,必须在运行时创建并填充数组。这就是你观察差异的原因。

String[]String...是同义词。如果你比较它们,你应该看到相同的表现。

答案 1 :(得分:8)

使用最新的JRE6和JRE7我得到的结果与你的结果不同,它们表明varargs的速度提高了5倍:

69
69
311

但是,我不会妄下结论,因为这个基准有几个缺点:函数中没有使用参数;该功能没有做任何事情;参数具有相同的值。 JIT可以轻松优化此代码和内联函数调用。我修改了你的例子来解决上面提到的明显问题并得到了以下结果:

627
7470
7844

结论是:不要犹豫使用varargs 。如果你的函数是微不足道的,那么JIT会调用它的调用,如果没有,那么varargs的开销可能会微不足道。

答案 2 :(得分:1)

有趣的问题!

这只是一个猜测:在幕后,Var-args只是数组。创建这个隐式数组并使用var-args参数填充它可能需要一些时间;因此性能受到了打击。好吧,我想。

答案 3 :(得分:0)

如上所述,使用var-args ...,

时会保留一个数组

您还应该尝试看到将“final”添加到每个方法的参数

的影响

我个人从2250开始有所改善 - &gt;阵列为2234 ms。

答案 4 :(得分:0)

我重构了一些代码。现在,我使用int而不是String导致字符串以池结尾并且是不可变的,而且我不知道这如何影响编译器优化。

我还要重新创建值,这样它们就不会成为常量,这也可能会影响编译器的优化。

我对方法做了一些事情,也针对编译器优化。

public class Test {


    static int n = 100_000_000;
    static int[] all_string = new int[n*1*5];
    static int all_strings_index = 0;



    public static void main(String[] args) {

        while(true) {
    
            all_strings_index = 0;
    
            int s1 = (int) System.nanoTime();
            int s2 = (int) System.nanoTime();
            int s3 = (int) System.nanoTime();
            int s4 = (int) System.nanoTime();
            int s5 = (int) System.nanoTime();
    
            long t = System.currentTimeMillis();
    
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                var(s1, s2, s3, s4, s5);
            }
            System.err.println("varargs    "+(System.currentTimeMillis() - t));
    
            all_strings_index = 0;
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                par(s1, s2, s3, s4, s5);
            }
            System.err.println("parameters "+(System.currentTimeMillis() - t));
    
    
            all_strings_index = 0;
    
            int[] arr = new int[] {s1, s2, s3, s4, s5};
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                var2(arr);
            }
            System.err.println("array      "+(System.currentTimeMillis() - t));
            System.err.println();
    
        }

    }

    static void par(int a1, int a2, int a3, int a4, int a5) {
        all_string[all_strings_index++] = a1;
        all_string[all_strings_index++] = a2;
        all_string[all_strings_index++] = a3;
        all_string[all_strings_index++] = a4;
        all_string[all_strings_index++] = a5;
    }

    static void var(int... a) {
        for (int s : a) {
            all_string[all_strings_index++] = s;
        }
    }

    static void var2(int[] a) {
        for (int s : a) {
            all_string[all_strings_index++] = s;
        }
    }
}
varargs    981
parameters 415
array      687

varargs    962
parameters 434
array      411

varargs    975
parameters 469
array      439

varargs    983
parameters 462
array      447

varargs    999
parameters 470
array      439

varargs    1018
parameters 475
array      455

varargs    1014
parameters 467
array      440

可变参数慢很多。