Еще Немного О Производительности Java

«Есть большая ложь, есть маленькая ложь, а также статистика производительности Java».

В последнее время я заметил множество тестов производительности Java с удивительно широким диапазоном результатов.

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

Многие понимают, что такие тесты — часть маркетинга.

А те, кто это заказывает, заказывают и результат. Но самое удивительное в этой ситуации то, что во лжи некого винить.

Нельзя отрицать истинность ни тех тестов, в которых Java на порядок уступает C#, ни тех тестов, в которых Java превосходит C++ (не C).

Проблема в том, что такие тесты ориентированы в основном на начинающих разработчиков, чтобы переманить в свой лагерь больше программистов и тем самым повысить коммерческую привлекательность платформы для бизнеса.

И в таких тестах намеренно упускаются труднопонятные детали работы динамической JIT-компиляции и адаптивной оптимизации HotSpot, в которые начинающие программисты редко вникают. Что это значит? Банальный когнитивный диссонанс.

Когда-то я тоже писал сравнительный тест программы на Delphi и Java. И я получил почти десятикратное отставание в производительности Java. Я, конечно, мог всё приписать своим рукам, поэтому постарался разобраться в механизме JIT-компиляции.

Ведь если дело не в ваших руках, а Java действительно создает такой неэффективный нативный код, то вам нужно держаться как можно дальше от такой платформы.

Но реальность говорит, что это один из самых распространенных промышленных языков программирования, и с такими потерями производительности бизнес не смирится.

Я попытаюсь на пальцах объяснить, как я понял суть происходящего в недрах Java, и если я в чем-то ошибся, то поправьте меня.

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

Начнем с того, что адаптивная JIT-компиляция — довольно дорогая процедура, а компилировать весь код сразу — слишком дорого.

Да, в этом нет необходимости.

По статистике промышленных приложений, 80% времени программы тратится на 20% кода.

Именно эти 20% выгоднее всего накопить.

Остальная часть кода не окажет существенного влияния на производительность, даже если она будет выполнена в режиме интерпретации.

Для того, чтобы посчитать эти 20% нужно досконально изучить алгоритм.

А для изучения всех алгоритмов HotSpot требуется сбор значимой статистики, поэтому вначале вся программа работает в режиме интерпретации.

По мере накопления статистики компилятор компилирует приложение по частям.

Минимальной единицей компиляции является функция.

В конфигурации клиента перед началом работы JIT-компилятора выделяется 1500 вызовов функций для сбора статистики.

Для конфигурации сервера, где проводится более агрессивная оптимизация, требуется 15 000 вызовов.

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

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

В процессе оптимизации компилятор выдвигает гипотезы о функционировании кода и путем анализа статистики пытается доказать или опровергнуть свои предположения.

Если HotSpot опровергает свою гипотезу, то JIT-компилятор выполняет деоптимизацию для последующей перекомпиляции уже скомпилированного кода.

И проводит новые оптимизации на основе новых гипотез.

Чаще всего HotSpot строит гипотезы о структуре алгоритма, используя стандартные шаблоны проектирования.

Вот почему всех Java-программистов учат использовать шаблоны проектирования как можно чаще.

Даже если вы сможете придумать оригинальный эффективный алгоритм, это не значит, что ваша программа будет работать быстрее, скорее наоборот. HotSpot будет слишком часто выдвигать ложные гипотезы о вашем коде, деоптимизируя и перекомпилируя его, пока не найдет оптимальный способ компиляции вашего алгоритма.

И не факт, что JIT-оптимизация нестандартного алгоритма окажется лучше, чем оптимизация громоздкого паттерна.

Инженеры SUN в свое время проделали большую работу, чтобы научить Java эффективно работать с шаблонами.

Среди распространенных оптимизаций HotSpot может конвертировать медленные вызовы виртуальных функций во встроенные замены.

Но для этого HotSpot необходимо знать обо всех полиморфных преобразованиях в вашей программе.

Зная, где чаще всего вызывается определенная функция, компилятор перемещает ее как можно ближе к тому месту, где она используется.

Аналогичного уровня производительности можно достичь в C, управляя встроенными вызовами вручную.

Но при использовании объектно-ориентированной парадигмы в C++ это сложно, а иногда и невозможно.

Следовательно, при достаточно большом наборе статистики HotSpot может приблизиться по производительности к C и превзойти C++.

А если мы используем шаблоны, то HotSpot распознает их и делает необходимые замены с первого раза.

Но если у вас нестандартный алгоритм, то в этом случае гипотезы HotSpot о проведенных подменах с большей вероятностью окажутся ложными.

И хотя для более эффективной оптимизации будет собираться новая статистика, JIT-компилятор работает со строго заданными интервалами итераций.

По умолчанию, как уже говорилось выше, для десктопных программ это 1500 итераций.

Это может показаться слишком большим значением.

В этом случае поведение HotSpot можно изменить с помощью параметра -XX:CompileThreshold. Таким образом мы сможем сократить время, необходимое для прогрева HotSpot. Но сокращая время прогрева, мы не сократим время сбора статистики.

Статистика собирается на основании активности программы.

Поэтому, установив слишком маленькое значение -XX:CompileThreshold=1, мы лишь заставим JIT-компилятор очень часто простаивать, особенно после запуска, когда новая статистика поступает постоянно.

Это также исказит статистику производительности.

При современной мощности процессора оптимальным значением можно считать -XX:CompileThreshold=100. Но ради интереса рекомендую поэкспериментировать с этим параметром, так как он очень чувствителен к алгоритму.

Для мониторинга работы JIT-компилятора используйте параметр -XX:+PrintCompilation в консоли.

Уверяю вас, это будет очень познавательное наблюдение.

Таким образом, результат теста производительности зависит от того, как долго HotSpot собирал статистику по алгоритму.

А если нет статистики, то нет и JIT-компиляции.

При измерении статистики Java нам необходимо четко понимать, что именно мы измеряем: производительность интерпретатора, производительность неоптимизированного кода или производительность оптимизированного кода.

Иначе получится как в анекдоте: «Не важно, как проголосовали, важно, как посчитали».

Вместо эпилога.

Можно ли считать такую архитектуру преимуществом или недостатком? На этот вопрос каждый ответит лично для себя и каждый будет прав.

Java может решить все поставленные перед ней задачи, но нужно хорошо знать, как она это может делать, и иногда в чем-то приходится идти на компромиссы.

Но программисты, как никто другой, знают, что такое радость творчества.

Самый мощный мотиватор работы программиста – это радость и удовлетворение от созерцания результата своего труда.

Именно поэтому работодателям так сложно мотивировать нас деньгами.

[1] Мы (по крайней мере, большинство из нас) любим свою работу, потому что результат нашей работы приносит нам радость, и в то же время, как приятный бонус, еще и хорошо оплачивается.

Вот почему мы любим инструменты, которые помогают нам быть счастливее и строить творческие отношения с нашими клиентами, коллегами и сообществами.

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

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

Если вы не получаете удовольствия от своей работы, значит, вы делаете что-то не то, чем вам следовало бы заниматься.

Делайте то, что сделает вас по-настоящему счастливым, делайте то, что вы действительно можете – сделайте мир лучше.

Будь счастлив.

Пить кофе.

Напишите Java. UPD: Исправлены опечатки.

Большое спасибо Зейдж для редактирования текста.

Теги: #java #hotspot #статистика #производительность #java

Вместе с данным постом часто просматривают: