Как создать повторно используемое мгновенное (недолговечное) событие Java FlightRecorder JFR?

#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 , включен ли он или нет, и отложив создание события.