views:

266

answers:

3

Coding i came around to check for the vararg performance of Java.

I write following test code:

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) {
    }
}

On my machine the average output is:

78
4696
78

Seems that pass variables to methods is at no cost ?! Good !

But using varags is 60x slower ! Why ?

An explanation could be that the program must create the array on the heap and the time is spend by GC. But for less loops i still get as output:

0
62
0

What is spending this extra time for and anyway the compiler has all information to resolve this to a fix variable call ...

Its not my intention to optimize for that, but i found this curious ...

Update

I added a new test

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

And this one argument version is still 30x slower. Maybe there is an ArrayList.toArray() behind the scene ?

So be aware of no-needed varags methods in your code and refactor to fix length. That could be a perfomance boost.

+5  A: 

Static list of arguments is quite different from an array. When you pass them that way, compiler reserves space for the references and populates them when the method is called.

Varargs is an equivalent of array. To call such a method, it's necessary to create and populate array at run time. That's why you observe the difference.

String[] and String... are synonyms. If you compared them, you should see identical performance.

Konrad Garus
Yup, it will be the same, as varargs are syntactic sugar transformed into array calls before effective compilation.
Riduidel
This is basically right though I think you should clarify that the difference is that varargs requires the JVM to allocate and populate an array. Whether it's on the heap or stack isn't quite the issue (though yes of course it's on the heap).
Sean Owen
@Sean Owen Thanks, updated.
Konrad Garus
+1  A: 

Interesting problem !

This is just a guess : behind the scenes, Var-args are just arrays. Creating this implicit array and populating it with the var-args parameters may take some time; hence the performance hit. Well, I guess.

Olivier Croisier
A: 

As said, an array is maintained when using var-args...,

you should also try to see the influence of adding "final" to the parameters of every methods

i personally get an improvement from 2250 -> 2234 ms for the array.

ssaboum
Parameters in Java really can't be made final, they can still be changed, so this shouldn't influence anything.
Helper Method
How can a final parameter be changed?
Hardcoded
final arguments and variables can't be changed. I think the point is better stated thus: making an *object reference* final means you can't change the reference, but it does not mean the object it refers to can't change state.
Sean Owen
in other words, for primitive types you can't change them, for objects use setters or change its public members.
ssaboum