#java #profiling #jfr
Вопрос:
Я работаю над добавлением пользовательских событий записи полетов для отслеживания некоторых наших ключевых показателей эффективности и их отображения в дампах записи полетов.
Одна проблема, которая меня поражает, заключается в том, как пометить событие как событие, основанное на «мгновении» или «продолжительности».
https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm#JFRUH171
Из моего ограниченного тестирования кажется, что вызов commit
события, которое вы позже повторно используете (скажем, в локальном потоке), заставляет вторые и предстоящие вызовы commit
включать отметку времени окончания и продолжительность между вторым и первым фиксацией. Это на самом деле не соответствует моему текущему варианту использования, когда меня не интересует время между созданием события и вызовом фиксации.
Мне кажется, что если вы всегда создаете новые события и не вызываете begin
их, вы создаете событие, которое не имеет продолжительности, но по соображениям производительности я бы предпочел избежать накладных расходов, связанных с созданием новых событий каждый раз и ограничением выделения кучи.
Ответ №1:
Все события являются длительными в том смысле, что они содержат поле, называемое длительностью. Если вы звоните commit()
без вызова begin()
или end()
, продолжительность будет установлена равной 0. Это добавит один байт на событие в файл, и накладные расходы будут незначительными. Когда дело доходит до кэширования и повторного использования экземпляров событий, это не поддерживается, но может работать в некоторых сценариях.
Рекомендуемый способ — создать объект для каждого события. В большинстве случаев JIT может устранить выделение, если объект не экранируется. То есть оставьте метод, в котором вы используете объект события.
В этом видео описан процесс для отключенных событий, но некоторая оптимизация применима и к включенным событиям. https://youtu.be/plYESjZ12hM?t=1126
(Анализ экранирования, вероятно, со временем улучшится, и, возможно, примитивные объекты можно будет использовать в будущем. Кэширование в локальных потоках может оказаться анти-шаблоном, особенно в связи с тем, что в ближайшее время появятся виртуальные потоки)
Комментарии:
1. Спасибо, Кир! Это видео от Микаэля-лучшее объяснение анализа побега и устранения мертвого кода, которое я видел, хотелось бы получить больше примеров лучших практик, когда дело доходит до пользовательских событий. 2 последующих вопроса: — Большинство примеров пользовательских событий устанавливает изменяемые поля вместо того, чтобы передавать их конструкторам событий. Связано ли это с лучшими практиками устранения JIT или они взаимозаменяемы? — Внутренние события JVM, похоже, создают/фиксируют события, используя
EventHandlers
, а не методы экземпляра. Делается ли это по соображениям производительности, и должны ли мы стремиться к чему-то подобному в критически важном для производительности коде?2. Одной из причин установки полей вместо использования конструктора является защита дорогостоящих операций (которые невозможно устранить) с помощью «if (event.shouldCommit()) { event.x = foo(); … }» В других случаях я не знаю, быстрее ли конструктор или нет. Когда дело доходит до внутренних событий, мы заметили, что JIT не смог устранить выделение для некоторых событий (возможно, из-за обработки исключений). bugs.openjdk.java.net/browse/JDK-8187234 итак, у нас есть несколько временных взломов в JDK.
3. Хорошо, это хороший момент в том, чтобы держать конструкторов тонкими. Я обошел это, спросив кэш
EventType
, включен ли он или нет, и отложив создание события.