Легко подумать, что от профилирования не стоит ожидать больших новостей: поскольку разработчики профилируют уже десятилетиями, до чего там можно было ещё не додуматься? Но в Java-профилировании кроются серьёзные подводные камни вроде safepoint bias, и появляются новые инструменты для решения подобных проблем.
Андрей apangin Паньгин (Одноклассники) недавно создал один из таких инструментов: на конференции JPoint в апреле он представил опенсорсный проект async-profiler, которому safepoint bias не страшен. Другой эксперт в этой теме — Нитсан Вакарт: о той же проблеме с safepoints многие узнали благодаря его блог-посту «Why (Most) Sampling Java Profilers Are Fucking Terrible».
Мы решили поговорить с ними обоими сразу, начав разговор с последних новостей об async-profiler, а позже перейдя к состоянию Java-профилирования в целом.
JUG.ru: Недавно async-profiler был перемещён на GitHub из личного репозитория Андрея в “jvm-profiling-tools” — что представляет собой jvm-profiling-tools, и чем вызвано перемещение?
Нитсан: Это объединение нескольких репозиториев, преследующих схожие цели. Идея в том, чтобы собрать их в одном месте и стимулировать сообщество активнее их развивать.
Андрей: Именно. Когда стало понятно, что async-profiler интересен Java-сообществу, я решил его перенести в нейтральное место, потому как сторонним разработчикам порой некомфортно контрибьютить в чужой личный репозиторий.
Нитсан: В jvm-profiling-tools есть Honest Profiler, async-profiler и perf-map-agent, и они подходят к одной теме с разных сторон: perf-map-agent открывает возможности для профилирования с использованием линуксового perf, Honest Profiler использует AsyncGetCallTrace, что позволяет избежать safepoint bias, а async-profiler совмещает то и другое очень приятным образом.
Андрей: Да. На самом деле, в идее сочетания обоих подходов ничего космического нет, но почему-то никому в голову это раньше не приходило.
Нитсан: Вообще это было в Solaris Studio, но проблема Solaris Studio в том, что им пользуются человек 20 в мире.
Андрей: Но, насколько я знаю, он не показывает вызовы ядра, так ведь?
Нитсан: Показывает нативный код, но не ядро.
JUG.ru: Поскольку у Honest Profiler и async-profiler есть общее преимущество «отсутствие safepoint bias», теперь при взгляде на jvm-profiling-tools у новичков в профилировании может появиться вопрос: «Ну и какой из двух похожих инструментов мне использовать?» Что вы можете им сказать?
Андрей: Я считаю, что в отношении точности профилирования и полноты информации async-profiler далеко впереди. Ведь даже сам AsyncGetCallTrace в HotSpot работает не всегда: в некоторых пограничных случаях JVM не может восстановить стек-трейс, хотя async-profiler справляется и с такими ситуациями. Кроме того, Honest Profiler вообще не показывает нативные стек-трейсы. Но его большое преимущество в инфраструктуре вокруг представления данных. Он умеет отображать результаты красиво, у него есть UI, а async-profiler — это просто Java-агент, запускаемый из консоли.
Нитсан: Я думаю, что будущее за async-profiler. И мне бы хотелось, чтобы некоторые возможности Honest Profiler попали в async-profiler. Есть ещё разница в том, что Honest Profiler работает на macOS, а async-profiler пока нет: поддерживать большое сообщество хипстеров в нашей отрасли — это важно.
JUG.ru: Вроде бы Вадим Цесько из Одноклассников уже делал возможной работу async-profiler на macOS?
Андрей: Это было до того, как я добавил поддержку perf events. Linux-специфичный вызов сломал поддержку macOS. Но есть и хорошие новости: буквально на днях я разговаривал с Норманом Маурером из Apple (автором Netty), ему также интересен async-profiler, и он любезно согласился сделать Mac-порт.
JUG.ru: В июле в async-profiler появился профайлер хипа — можете рассказать об этом?
Андрей: Существует два основных подхода к профилированию памяти в Java. Первый — это инструментация байткода. Но для продакшен-систем он ужасен, потому что плачевно сказывается на производительности. Ряд оптимизаций компилятора перестаёт работать: прежде всего, Escape Analysis больше не помогает избегать аллокаций в хипе.
Другой подход — использование DTrace probes, что тоже крайне накладно, и может включаться только на старте JVM.
Но это не всё. Есть ещё гораздо более эффективный подход, основанный на сэмплировании TLAB (Thread Local Allocation Buffer). Он реализован в Java Mission Control / Java Flight Recorder, но требует включения коммерческих функций Oracle JDK, а с OpenJDK вообще не работает. Похожий метод используют внутри Google, но там требуется сборка модифицированной версии JVM.
Я нашёл способ использовать этот подход без подключения платных функций, в том числе, с OpenJDK. Сейчас не буду погружаться в подробности, но в отдельном докладе обязательно расскажу.
Нитсан: Я думаю, это важно. Поскольку Java Mission Control сейчас, пожалуй, единственный инструмент для профилированием аллокаций, и работа с подобными процессами в JMC реализована очень своеобразно, многие люди просто не занимаются таким профилированием. Надеюсь, что это поможет профилированию аллокаций стать мейнстримом.
JUG.ru: Может показаться странным, что заметные подвижки в профилировании происходят в 2017-м, когда они были бы полезны годы назад. В чём причина такой задержки?
Андрей: Java — корень всех зол :) Она делает жизнь одновременно и лучше, и хуже. С одной стороны, из-за особенностей JVM стандартные подходы становятся неприменимы, но с другой — JVM предоставляет свои собственные API для профилирования.
Нитсан: Я думаю, что мир Java похож на Windows. Windows была ужасной ОС (вероятно, сейчас куда лучше), но, страдая от многих недостатков, она одновременно имела большой успех. Та же история и с Java. В случае с инструментами профилирования у Java получилось плохо. Я не совсем уверен в том, почему это так.
Я думаю, что разработчики JVM традиционно использовали Solaris Studio, так что всё более-менее нормально работало, но только для них. Специалисты использовали специализированные инструменты. А большинство Java-разработчиков были довольны тем, что имели.
Но теперь Java пришлось столкнуться с реальностью. Нативное профилирование, которое работало в Solaris Studio, но тогда было нишевым решением, становится все более популярным.
Андрей: Считаю нужным добавить, что Java не равно HotSpot, и другие JVM могут быть более дружелюбны к профайлерам.
Нитсан: Может быть, я что-то упустил, но о какой JVM мы говорим? Я знаю много о Zing, и у меня есть немного опыта работы с IBM J9…
Андрей: Сейчас на мне футболка Excelsior JET, и поэтому я вспомнил об этом проекте. Он умеет прекомпилировать Java в нативный код, и, насколько я знаю, от safepoint bias не страдает.
Нитсан: А, ок. Никогда этим не пользовался. Полагаю, что в этом случае можно сразу брать нативный профайлер.
JUG.ru: AsyncGetCallTrace, который используют async-profiler и Honest Profiler, не является официальным API. Не ощущается ли его использование «хаком»? Не беспокоитесь ли, что в будущем он может перестать функционировать? Помогла бы более официальная поддержка Oracle в вопросах профилирования?
Нитсан: AsyncGetCallTrace работает ещё с запуска OpenJDK 6, так что похоже, что он всегда был и будет работать. Это «незаконнорожденный ребенок», но я не думаю, что его могут взять и выкинуть. Когда что-либо становится опцией JVM, это в некотором роде получает официальную поддержку. Так что думаю, нам не стоит слишком беспокоиться об этом. Хотя мне интересно, насколько хорошо AsyncGetCallTrace уживается с новым компилятором Graal.
Конечно, более официальная поддержка помогла бы. На данный момент Oracle предоставляет JMC в качестве платного варианта, а всему остальному Java-миру остаётся что-то вроде VisualVM. Сейчас в этом деньги. Я думаю, что Oracle испытывает конфликт интересов: интересы Java, с одной стороны, и их собственные — с другой. Можно сказать, что для них поспособствовать улучшению других JVM-профайлеров означало бы ухудшить собственное положение.
Я не утверждаю, что они активно переживают из-за этого. Я понятия не имею, чего они сейчас хотят. Возможно, они сделают доступными для всех вещи вроде JMC. Теперь, когда работа над Jigsaw закончена, у них есть много времени для другого.
Андрей: Соглашусь, что AsyncGetCallTrace — отчасти «хакерский» API. К тому же далеко не идеальный: я и сам сообщал о багах. Но пока что это лучшее, что есть в HotSpot JVM.
JUG.ru: А может ли, помимо уже имеющихся вещей вроде AsyncGetCallTrace, появиться что-то ещё, что облегчит жизнь создателям профайлеров?
Андрей: Да. Недавно в списках рассылки HotSpot обсуждалось профилирование аллокаций. В итоге даже появился проект JEP, который предлагает новый стандартизованный API для сэмплирования хипа. Думаю, что поднимать такие темы в рассылках и предлагать JEP — правильный путь. Так что, возможно, когда-нибудь в Java 11…
JUG.ru: Что вы думаете о будущем Java-профилирования независимо от действий Oracle? Профайлеры станут намного лучше, чем сейчас?
Андрей: Очень на это надеюсь. В своих докладах я стараюсь доносить до разработчиков мысль, что у профайлеров, которые они используют сейчас, есть большие подводные камни. Нужно либо прекратить использовать их, либо как следует разобраться в их недостатках и ловушках. И думаю, когда больше людей осознают масштабы проблемы, разработчики этих инструментов примутся их улучшать.
Нитcан: В ряде вопросов есть куда расти. С perf-map-agent мы обрели возможность отслеживать инлайнинг при профилировании, но перейдя к async-profiler, снова её теряем. Мне бы очень хотелось увидеть её снова воплощённой.
Другая область — визуализация. Если использовать async-profiler при работе с многопоточными приложениями, где один поток задействует 100% CPU, а все остальные просто висят в ожидании, при профилировании можно получить сбивающую с толку картину. Меня интересуют проблемы представления данных, и я уверен, что существует множество подобных проблем.
Андрей: Да. Сегодня FlameGraph очень популярен в качестве визуализации, но я бы сказал, что он далеко не совершенен.
JUG.ru: Вы оба уже говорили (в докладах и блог-постах), что профайлеры могут создавать искажённую картину. Считаете ли вы, что отрасли сильно вредит использование людьми этой искажённой картины? Может ли быть так, что индустрии от профайлеров тогда вообще больше вреда, чем пользы?
Нитсан: Да, я думаю, что это вредит индустрии. Если вы посмотрите в интернете обсуждения производительности Java, то увидите, что там полно булшита. И причина, по которой его так много, в том, что информацию сложно верифицировать.
Некоторые люди говорили мне, что проблема никогда не оказывается в HashMap. И причина, по которой они никогда не думали, что HashMap может быть проблемой, состоит в том, что обычный профайлер им этого никогда не покажет. Я не утверждаю, что конкретно в их случае проблема в нём, но как бы то ни было, они об этом никогда не узнают. А когда они смотрят на график расхода времени CPU, они не могут увидеть время, затраченное на GC. То есть в том случае, если «бутылочным горлышком» у них оказался GC, они никак не смогут этого отследить.
Андрей: Я согласен с Нитсаном, но добавлю, что лучше иметь хотя бы плохой инструмент, чем не иметь никакого. Главная проблема — не когда профайлер привирает, а когда он не используется совсем. Многие разработчики вообще не профилируют, хотя зачастую проблема производительности кроется в неэффективных алгоритмах, и любой сэмплирующий профайлер её легко выявит.
Нитсан: Я согласен, что многие люди не профилируют, и это проблема. Но если единственным вашим инструментом оказывается плохой профайлер… Вы смотрите на него, видите, что он выдаёт какую-то бессмыслицу, и говорите остальным: «Лучше мы будем просто ставить временные метки», таким у вас получается вывод, и несложно понять, как подобное отбивает у людей желание использовать профайлеры.
JUG.ru: Значит, чтобы улучшить ситуацию с Java-профилированием, нам всем надо работать не только над улучшением инструментов, но и над знаниями сообщества о них?
Нитсан: Да. Я думаю, масштабная история успеха в Java-мире — это JMH, Java Microbenchmarking Harness. И причина в том, что это решение было очень успешным не только с технологической точки зрения, но и в аспекте обучения пользователей, предоставления им возможности лучше познакомиться с этой областью.
Думаю, то, что Андрей и создаёт инструменты, и рассказывает людям, очень важно.
Андрей: Даже самые мощные инструменты будут бесполезны, если не уметь ими пользоваться. С тем же JMH: я много раз видел, как люди писали ерунду в JMH, а потом делали совершенно неправильные выводы. Обучение является неотъемлемой частью успеха.
JUG.ru: Вы оба собираетесь помочь этому обучению своими докладами на ближайшем Joker, и вы оба собираетесь представить там новые версии докладов, ранее представленных на других конференциях. В чём будет состоять новизна?
Нитсан: После того, как я представил на QCon доклад “Profilers are lying hobbitses”, я подумал, что самое лучшее в нём — это название. Поэтому я решил сохранить название, но сам доклад будет очень сильно отличаться. Мы снова будем говорить о профайлерах и о том, как они могут ввести нас в заблуждение, но, думаю, я начну с самого мрачного, а затем буду показывать, как выкарабкаться. В прошлом доклад был серией сюрпризов, приводивших к выводу «ничего не работает». В этот раз будет так: «ничего не работает, но посмотрим, как нам с этим справиться».
Андрей: Изначально я планировал показать продолжение истории async-profiler, начатой на JPoint 2017. Однако потом мы с Программным Комитетом обнаружили большое сходство моего доклада с докладом Нитсана, так что я решил взять новую тему. Пока что я не готов сказать, что именно это будет, но в ближайшее время в программе Joker 2017 можно будет увидеть мой новый доклад! Так что следите за обновлениями.
Java-конференция Joker, где выступят Нитсан и Андрей, состоится в Петербурге 3-4 ноября. Как обычно, после докладов спикеры Joker оказываются в дискуссионных зонах, так что там можно будет расспросить их о профилировании лично. А помимо Андрея и Нитсана, там будут десятки других спикеров — на сайте конференции можно увидеть программу (и приобрести билет).
Автор: Евгений Трифонов