В приведенном ниже коде указаны две простые функции по 10 миллиардов раз каждый.Почему два аргумента строки более эффективны, чем один аргумент списка
public class PerfTest {
private static long l = 0;
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b");
long time1 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func1("a", "b");
}
long time2 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func2(list);
}
System.out.println((time2 - time1) + "/" + (System.currentTimeMillis() - time2));
}
private static void func1(String s1, String s2) { l++; }
private static void func2(List<String> sl) { l++; }
}
Мое предположение заключалось в том, что выполнение этих двух вызовов будет близким к идентичному. Если что-нибудь, я бы догадался, что прохождение двух аргументов будет немного медленнее, чем передача одного. Учитывая, что все аргументы являются объектными ссылками, я не ожидал, что один из них представляет собой список, который имеет значение.
Я провел тест много раз, и типичным результатом является «12781/30536». Другими словами, вызов с использованием двух строк занимает 13 секунд, а вызов с использованием списка занимает 30 секунд.
Каково объяснение этой разницы в производительности? Или это несправедливый тест? Я попытался переключить два вызова (в случае, если это было вызвано эффектами запуска), но результаты одинаковы.
Update
Это не справедливо тест по многим причинам. Однако он демонстрирует реальное поведение компилятора Java. Обратите внимание на следующие два дополнения, чтобы продемонстрировать это:
- Добавление выражений
s1.getClass()
иsl.getClass()
к функциям делает два вызова функции perfom тот же - Запуск теста с
-XX:-TieredCompilation
также делает две функции вызовы выполняют ту же
Объяснение этого поведения приведено в принятом ниже ответе. Очень краткое изложение ответа @ apangin заключается в том, что func2
не встроен компилятором hotspot, потому что класс его аргумента (т. Е. List
) не разрешен. Принудительное разрешение класса (например, с использованием getClass
) заставляет его быть встроенным, что значительно улучшает его производительность. Как было указано в ответе, нерешенные классы вряд ли произойдут в реальном коде, что делает этот код нереалистичным краевым случаем.
Можете ли вы добавить, что вы ожидали и почему? – ChiefTwoPencils
@ChiefTwoPencils добавили этот параграф. – sprinter
Я не проголосовал за закрытие, но если кто-то не захочет взломать среду выполнения, чтобы посмотреть на конкретные оптимизации компиляции, большинство вопросов производительности на самом деле не очень полезны (хотя они могут быть забавными/интересными), и ответы могут перейти от выпуска к выпуску. В этом случае я просто предполагаю, что JVM усложнил компиляцию или запоминание двух вызовов параметров, чем вызов массива, но серьезно - просто напишите, что является самым читаемым! Также обратите внимание, что наиболее читаемая версия часто такова, что оптимизация JVM оптимальна. –