Почему JVM требуется прогрев?

Я понимаю, что в виртуальной машине Java (JVM) потенциально требуется разогрев, поскольку Java загружает классы с использованием процесса отложенной загрузки, и поэтому вы хотите убедиться, что объекты инициализированы, прежде чем вы начнете основные транзакции. Я разработчик на C ++, и мне не приходилось сталкиваться с подобными требованиями.

Однако я не могу понять следующие части:

  1. Какие части кода нужно разогреть?
  2. Даже если я разогреваю некоторые части кода, как долго он остается теплым (при условии, что этот термин означает только то, как долго объекты вашего класса остаются в памяти)?
  3. Как это помогает, если у меня есть объекты, которые нужно создавать каждый раз, когда я получаю событие?

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

Обратите внимание, что приложение предназначено для высокочастотной торговли (HFT), поэтому производительность чрезвычайно важна.


person Suparna    schedule 24.03.2016    source источник
comment
Вы можете найти -XX:+PrintCompilation полезным для отслеживания поведения компиляции. Вы также можете связаться с oracle, они работают над компилятором AOT, который в настоящее время предлагается только коммерческим клиентам AIUI. Я думаю, что некоторые другие поставщики JVM также предлагают AOT.   -  person the8472    schedule 24.03.2016


Ответы (7)


Какие части кода нужно разогреть?

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

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

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

Даже если я разогреваю некоторые части кода, как долго он остается теплым (при условии, что этот термин означает только то, как долго объекты вашего класса остаются в памяти)?

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

Как это помогает, если у меня есть объекты, которые нужно создавать каждый раз, когда я получаю событие?

Если вам нужна низкая задержка или высокая производительность, вы должны создавать как можно меньше объектов. Я стремлюсь производить менее 300 КБ / сек. С такой скоростью распределения у вас может быть достаточно большое пространство Eden, чтобы собирать небольшие деньги один раз в день.

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

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

Обратите внимание, что приложение предназначено для высокочастотной торговли (HFT), поэтому производительность чрезвычайно важна.

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

http://chronicle.software/

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

В частности, вас может заинтересовать https://github.com/OpenHFT/Java-Thread-Affinity, поскольку это Библиотека может помочь уменьшить джиттер планирования в ваших критических потоках.

Также сказано, что критические участки кода, требующие разогрева, должны запускаться (с фальшивыми сообщениями) не менее 12К раз, чтобы он работал оптимальным образом. Почему и как это работает?

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

person Peter Lawrey    schedule 24.03.2016
comment
Спасибо @PeterLawrey за такое подробное объяснение. Единственное, что я хотел бы задать в качестве дополнительного вопроса, это то, можно ли регистрировать или отслеживать, какой раздел вашего кода нагревается. - person Suparna; 25.03.2016
comment
@Suparna: каждая часть критического пути должна быть разогрета, включая TCP-соединение, особенно те, которые не часто вызываются. Я предлагаю Chronicle Queue для ведения журнала с малой задержкой и постоянного обмена сообщениями. - person Peter Lawrey; 25.03.2016
comment
@PeterLawrey, сэр, я помню, что в одном из ваших видео у вас было какое-то решение (вероятно, разработанное вами), которое помогало разминке, я помню, что что-то вроде того, что оно сохранит профиль, и при следующем запуске jvm предварительно скомпилирует некоторые методы с использованием предыдущего профиля, следовательно, не дожидаясь для 10k казней ... я мог бы быть полностью отключен, потому что это было давно ... У вас было что-нибудь для разминки jvm? - person vach; 06.08.2017
comment
@vach У меня есть инструмент, который использует класс WhiteBox, однако а) он не поддерживается и б) не очень помогает. Лучшее решение - самостоятельно прогреть код реалистичной нагрузкой. т.е. используйте один из ваших нагрузочных тестов. - person Peter Lawrey; 07.08.2017
comment
@PeterLawrey Даже после того, как ваш код нагрелся, вы должны следить за тем, чтобы кеш-память процессора оставалась теплой, как обеспечить, чтобы кеш-память процессора оставалась теплой? - person Govinda Sakhare; 22.02.2021
comment
@GovindaSakhare отключите управление питанием, используйте изолированные процессоры, используйте регулятор производительности, ожидание занятости, периодически запускайте код, чтобы сохранить его в кеше. Вот статья, которую я написал о том, как один и тот же фрагмент кода может работать с очень разными скоростями в зависимости от того, что было запущено до него. chronicle.software/ - person Peter Lawrey; 01.03.2021

Под потеплением понимается выполнение фрагмента кода достаточное количество раз, когда JVM перестает интерпретировать и компилируется в собственный (по крайней мере, в первый раз). Обычно это то, чего вы не хотите делать. Причина в том, что JVM собирает статистику по рассматриваемому коду, который она использует во время генерации кода (аналогично оптимизации, управляемой профилями). Поэтому, если рассматриваемый фрагмент кода «нагрет» поддельными данными, которые имеют свойства, отличные от реальных данных, вы вполне можете снизить производительность.

РЕДАКТИРОВАТЬ: поскольку JVM не может выполнять статический анализ всей программы (он не может знать, какой код будет загружен приложением), вместо этого она может делать некоторые предположения о типах на основе собранной статистики. Например, при вызове виртуальной функции (на языке C ++) в точном месте вызова, когда она определяет, что все типы имеют одинаковую реализацию, тогда вызов переводится в прямой вызов (или даже встроенный). Если позже это предположение окажется ошибочным, тогда старый код должен быть «некомпилирован», чтобы вести себя должным образом. AFAIK HotSpot классифицирует call-сайты как мономорфные (единственная реализация), би-морфные (ровно два..преобразованных в if (imp1-type) {imp1} else {imp2}) и полностью полиморфные..виртуальные отправления.

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

person MB Reynolds    schedule 24.03.2016
comment
Я предполагаю, что количество раз может быть как минимум 12К раз. После того, как JVM компилирует код в собственный, гарантированно ли она останется в этом состоянии до конца времени обработки? - person Suparna; 24.03.2016
comment
@Suparna Нет. JIT может распаковать (и перекомпилировать) код, если сочтет это необходимым. - person Kayaman; 24.03.2016
comment
@Kayaman - Существуют ли какие-либо приемы / методы, которым можно следовать во время разработки, чтобы продлить время, в течение которого JIT не распаковывает / перекомпилирует код? - person Suparna; 24.03.2016
comment
@Suparna Вы можете управлять JIT с помощью параметров времени выполнения, но это не самая сильная моя область знаний. Попробуйте их и убедитесь. - person Kayaman; 24.03.2016
comment
Это старый, возможно, ответит на некоторые из ваших вопросов: slideshare.net/ZeroTurnaround/ - person MB Reynolds; 24.03.2016
comment
Обновлен мой ответ с обзором основных проблем с распаковкой / повторной компиляцией. - person MB Reynolds; 24.03.2016
comment
Вообще говоря, вы должны предполагать, что JIT умнее вас и сохраняет важные части программы настолько оптимизированными, насколько это необходимо. - person Louis Wasserman; 24.03.2016

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

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

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

person Kayaman    schedule 24.03.2016
comment
Возможно, это не так. Мое производственное приложение используется для высокочастотной торговли, и любая задержка может быть проблемой. Совершенно очевидно, что при запуске, если вы не прогреете свое приложение, это приведет к высокой задержке в несколько миллисекунд. После прогрева и оптимизации JVM код обеспечивает нужный уровень производительности. Мне интересно узнать, почему и как? - person Suparna; 24.03.2016
comment
@Suparna Если вы пишете HFT-код на Java, вы почти наверняка используете специальные методы, такие как Chronicle, для управления ресурсами вручную, и ни один из стандартных советов JVM не применим. - person chrylis -cautiouslyoptimistic-; 24.03.2016
comment
Если вы имеете дело с HFT, вы должны сказать это в своем вопросе. Это совершенно другой зверь, и обычные правила Java уже не действуют. Возможно, вы захотите посмотреть OpenHFT для получения дополнительной информации (это работа часто летающего пассажира StackOverflow г-на Питер Лоури). - person Kayaman; 24.03.2016
comment
@chrylis - да, есть различные методы, которые мы использовали для создания объектов или, так называемые, для уменьшения gc. Тем не менее, разминка все же сделана и обязательна. - person Suparna; 24.03.2016

Почему JVM требует прогрева?

Современные (J) виртуальные машины собирают статистику во время выполнения о том, какой код используется чаще всего и как он используется. Один (из сотен, если не тысяч) примеров - это оптимизация вызовов виртуальных функций (на жаргоне C ++), которые имеют место только при реализации. Эти статистические данные, по их определению, могут собираться только во время выполнения.

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

Какие части кода нужно разогреть?

Часть, которая имеет решающее значение для производительности вашего приложения. Важная часть состоит в том, чтобы «разогреть» его точно так же, как он используется при нормальном использовании, в противном случае будут выполнены неправильные оптимизации (и отменены позже).

Даже если я разогреваю некоторые части кода, как долго он остается теплым (при условии, что этот термин означает только то, как долго объекты вашего класса остаются в памяти)?

Трудно сказать, что JIT-компилятор постоянно следит за выполнением и производительностью. Если будет достигнут некоторый порог, он попытается что-то оптимизировать. Затем он продолжит мониторинг производительности, чтобы убедиться, что оптимизация действительно помогает. В противном случае это может неоптимизировать код. Также могут произойти вещи, которые сделают оптимизацию недействительной, например, загрузку новых классов. Я бы счел эти вещи непредсказуемыми, по крайней мере, не на основе ответа stackoverflow, но есть инструменты, которые сообщают вам, что делает JIT: https://github.com/AdoptOpenJDK/jitwatch

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

Одним из простых примеров может быть: вы создаете объекты внутри метода, поскольку ссылка выходит за пределы области действия метода, эти объекты будут храниться в куче и, в конечном итоге, будут собраны сборщиком мусора. Если код, использующий эти объекты, активно используется, он может оказаться встроенным в один большой метод, возможно, переупорядоченный до неузнаваемости, пока эти объекты не будут жить только внутри этого метода. В этот момент они могут быть помещены в стек и удалены при выходе из метода. Это может сэкономить огромное количество мусора и произойдет только после некоторой разминки.

С учетом всего сказанного: я скептически отношусь к мысли о том, что для разминки нужно делать что-то особенное. Просто запустите свое приложение и используйте его, и JIT-компилятор с этим справится. Если у вас возникнут проблемы, узнайте, что JIT делает с вашим приложением и как настроить это поведение или как написать свое приложение так, чтобы оно приносило максимальную пользу.

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

person Jens Schauder    schedule 24.03.2016

Какие части кода нужно разогреть?

На этот вопрос вообще нет ответа. Это полностью зависит от вашего приложения.

Даже если я разогреваю некоторые части кода, как долго он остается теплым (при условии, что этот термин означает только то, как долго объекты вашего класса остаются в памяти)?

Объекты остаются в памяти до тех пор, пока ваша программа имеет ссылку на них, без какого-либо специального использования слабых ссылок или чего-то подобного. Информация о том, когда ваша программа «имеет ссылку» на что-то, может быть немного более неясным, чем вы можете подумать на первый взгляд, но это основа для управления памятью в Java, и оно того стоит.

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

Это полностью зависит от приложения. В общем нет ответа.

Я рекомендую вам изучать и работать с Java, чтобы понять такие вещи, как загрузка классов, управление памятью и мониторинг производительности. Для создания экземпляра объекта требуется некоторое время, в целом для загрузки класса требуется больше времени (что, конечно, обычно выполняется гораздо реже). Обычно после загрузки класса он остается в памяти на протяжении всей жизни программы - это то, что вам следует понять, а не просто получить ответ. к.

Есть также методы, которые нужно изучить, если вы их еще не знаете. Некоторые программы используют «пулы» объектов, экземпляры которых создаются до того, как они действительно понадобятся, а затем передаются для обработки, когда в этом возникает необходимость. Это позволяет критичной по времени части программы избежать времени, затрачиваемого на создание экземпляров в критический по времени период. Пулы поддерживают коллекцию объектов (10? 100? 1000? 10000?) И при необходимости создают больше экземпляров и т.д. .

Было бы вполне возможно использовать достаточно памяти для более частого запуска сборки мусора и ЗАМЕДЛИТЬ СИСТЕМУ, КОТОРАЯ ВЫ ПРЕДНАЗНАЧАЛИ ДЛЯ Ускорения. Вот почему вам нужно понять, как это работает, а не просто «получить ответ».

Еще одно соображение - большая часть усилий, направленных на ускорение программ, тратится впустую, поскольку в них нет необходимости. Без обширного опыта работы с рассматриваемым приложением и / или измерения системы вы просто не знаете, где (и будет ли) оптимизация вообще будет заметна. Дизайн системы / программы, позволяющий избежать патологических случаев медлительности, ОЧЕНЬ полезен и почти не требует времени и усилий на «оптимизацию». В большинстве случаев это все, что нам нужно.

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

person arcy    schedule 24.03.2016
comment
Спасибо тебе за это. Я буду рассматривать каждую из упомянутых вами тем по очереди. - person Suparna; 24.03.2016

Все дело в компиляторе JIT, который используется в JVM для оптимизации байт-кода во время выполнения (потому что javac не может использовать расширенные или агрессивные методы оптимизации из-за независимого от платформы характера байт-кода)

  1. вы можете разогреть код, который будет обрабатывать ваши сообщения. На самом деле, в большинстве случаев вам не нужно делать это специальными циклами разогрева: просто дайте приложению запуститься и обработать некоторые из первых сообщений - JVM постарается сделать все возможное, чтобы проанализировать выполнение кода и произвести оптимизацию :) Ручной прогрев с поддельными образцами может дать еще худшие результаты

  2. код будет оптимизирован через некоторое время и будет оптимизироваться до тех пор, пока какое-либо событие в потоке программы не ухудшит состояние кода (после этого JIT компилятор снова попытается оптимизировать код - этот процесс никогда не заканчивается)

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

person Cootri    schedule 24.03.2016
comment
Я наткнулся на этот флаг JVM -xx: CompileThreshold, который по умолчанию установлен на 10000. Имеет ли это какое-либо отношение к тому, что вы упомянули в №1. - person Suparna; 24.03.2016
comment
Также сказано, что критические участки кода, требующие разогрева, должны запускаться (с фальшивыми сообщениями) не менее 12К раз, чтобы он работал оптимальным образом. Почему и как это работает? - person Suparna; 24.03.2016
comment
не рекомендуется изменять параметры JIT по умолчанию, если вы не уверены, что делаете - команда Sun и Oracle имеет большой опыт и базу кода, чтобы эмпирически найти значения, которые в целом являются хорошими и обеспечивают максимальную эффективность. Конечно, вы можете уменьшить значение CompileThreshold, но всегда есть недостатки - например, большее потребление памяти. - person Cootri; 24.03.2016

Я всегда представлял это так:

Вы, как (разработчик C ++), можете представить себе автоматизированный итеративный подход путем jvm компиляции / горячей загрузки / замены различных бит на части (воображаемый аналог) _2 _, _ 3 _, _ 4 _, _ 5_ вариантов (а иногда отменив их, если сочтет это необходимым)

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

На стандартном jvm время, необходимое для того, чтобы сниппет был рассмотрен для jit, устанавливается -XX:CompileThreshold, который по умолчанию равен 1500. (Источники и версии jvm различаются, но я думаю, что это для jvm8)

Кроме того, имеется книга, которая есть у меня под рукой под Host Performace JIT. Глава (стр. 59), что во время JIT выполняются следующие оптимизации:

  • Встраивание
  • Устранение блокировки
  • Виртуальное устранение звонков
  • Устранение записи в энергонезависимую память
  • Генерация собственного кода

РЕДАКТИРОВАТЬ:

относительно комментариев

Я думаю, что 1500 может быть достаточно, чтобы намекнуть JIT, что он должен скомпилировать код в собственный код и прекратить интерпретацию. вы бы согласились?

Я не знаю, это просто намек, но поскольку openjdk является открытым исходным кодом, давайте посмотрим на различные ограничения и числа в globals.hpp#l3559@ver-a801bc33b08c (для jdk8u)

(Я не разработчик jvm, это может быть совершенно неподходящее место для поиска)

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

В моем понимании - правда; особенно если вы имеете в виду -Xcomp (принудительная компиляция) - это blog даже заявляет, что он не позволяет jvm выполнять какое-либо профилирование - следовательно, оптимизацию - если вы не запускаете -Xmixed (по умолчанию).

Таким образом, срабатывает таймер, чтобы выбрать часто используемый собственный код и оптимизировать его. Вы знаете, как мы можем контролировать этот интервал таймера?

Я действительно не знаю деталей, но gobals.hpp, который я связал, действительно определяет некоторые частотные интервалы.

person birdspider    schedule 24.03.2016
comment
Спасибо. Это простая и полезная аналогия. У меня два вопроса по этому поводу. 1. Я думаю, что 1500 может быть достаточно, чтобы намекнуть JIT, что он должен скомпилировать код в собственный код и прекратить интерпретацию. вы бы согласились? 2. Компиляция кода в нативный не обязательно означает, что он также оптимизирован. Таким образом, срабатывает таймер, чтобы выбрать часто используемый собственный код и оптимизировать его. Вы знаете, как мы можем контролировать этот интервал таймера? - person Suparna; 25.03.2016