Java的varargs性能

2022-09-03 00:28:36

编码我来检查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方法,并重构以修复长度。这可能是一个性能提升。


答案 1

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

Varargs 是数组的等效项。若要调用此类方法,必须在运行时创建并填充数组。这就是为什么你观察到差异。

String[]并且是同义词。如果比较它们,您应该会看到相同的性能。String...


答案 2

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

69
69
311

但是,我不会匆忙得出结论,因为这个基准测试有几个缺陷:参数不在函数中使用;该函数不做任何事情;参数具有相同的值。JIT 可以轻松优化此代码和内联函数调用。我修改了您的示例以解决上述明显问题,并得到了以下结果:

627
7470
7844

结论是:不要犹豫,使用varargs。如果你的函数是微不足道的,那么它的调用将由JIT内联,如果不是,那么varargs的开销可能会忽略不计。


推荐