В приведенном ниже коде указаны две простые функции по 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 секунд.
Каково объяснение этой разницы в производительности? Или это несправедливый тест? Я попытался переключить два вызова (в случае, если это было вызвано эффектами запуска), но результаты одинаковы.
Обновление
Это не справедливый тест по многим причинам. Однако он демонстрирует реальное поведение компилятора Java. Обратите внимание на следующие два дополнения, чтобы продемонстрировать это:
- Добавление выражений
s1.getClass()
иsl.getClass()
к функциям делает два вызова функций одними и теми же - Выполнение теста с помощью
-XX:-TieredCompilation
также делает вызовы двух функций выполняющими те же самые
Объяснение этого поведения приведено в принятом ниже ответе. Очень краткое изложение ответа @apangin заключается в том, что func2
не встроен компилятором hotspot, потому что класс его аргумента (т.е. List
) не разрешен. Принудительное разрешение класса (например, с использованием getClass
) заставляет его быть встроенным, что значительно улучшает его производительность. Как указано в ответе, нерешенные классы вряд ли произойдут в реальном коде, что делает этот код нереалистичным случаем края.