Производительность Java varargs

Кодирование я подошел, чтобы проверить производительность vararg на 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 в вашем коде и рефакторе, чтобы исправить длину. Это может быть повышение производительности.

Ответ 1

Статический список аргументов сильно отличается от массива. Когда вы передаете их таким образом, компилятор резервирует пространство для ссылок и заполняет их при вызове метода.

Varargs является эквивалентом массива. Чтобы вызвать такой метод, необходимо создать и заполнить массив во время выполнения. Вот почему вы наблюдаете разницу.

String[] и String... являются синонимами. Если вы их сравнили, вы должны увидеть идентичную производительность.

Ответ 2

Используя как последние JRE6, так и JRE7, я получаю разные результаты, чем ваши, и они показывают, что varargs в 5 раз быстрее:

69
69
311

Однако я бы не стал делать выводы, потому что этот тест имеет несколько недостатков: параметры не используются в функции; функция ничего не делает; аргументы имеют одинаковое значение. JIT может легко оптимизировать этот код и встроенные вызовы функций. Я изменил ваш пример для решения вышеупомянутых очевидных проблем и получил следующие результаты:

627
7470
7844

Вывод: не стесняйтесь использовать varargs. Если ваша функция тривиальна, то ее вызов будет встроен JIT, и если это не так, накладные расходы varargs, вероятно, будут незначительными.

Ответ 3

Интересная проблема!

Это просто предположение: за кулисами Var-args - это просто массивы. Создание этого неявного массива и заполнение его параметрами var-args может занять некоторое время; отсюда и производительность. Ну, я думаю.

Ответ 4

Как сказано, массив поддерживается при использовании var-args...,

вы также должны попытаться увидеть влияние добавления "final" к параметрам всех методов

Я лично получаю улучшение от 2250 → 2234 мс для массива.