open atlas
↑ К треку
Наблюдаемость OBS · 07 · 05

Как flame graph строится из сэмплов и как использовать его в production

Одинаковые стеки схлопываются, алфавитная сортировка группирует родителей с детьми, а ширина — это число сэмплов. Зная алгоритм, никогда не перепутаешь x-ось с временем. Profiling интегрируется с SLO burn, deploy diff и capacity planning.

OBS Middle ◷ 15 min
Уровень
ОсновыJuniorMiddleSenior

Старший инженер на конференции спрашивает: «Какая функция выполняется раньше этой на flame graph?» Он указывает на два соседних фрейма на одном уровне. Ответ: ни один — x-ось алфавитная. Если этого не знать, потеряешь часы на неверную гипотезу.

Как flame graph строится из сэмплов

Каждый сэмпл — список имён функций от leaf (выполняется сейчас) до root (точка входа программы). После окна профилирования:

  1. Собираются все сэмплы.
  2. Одинаковые стеки схлопываются в одну колонку — их счётчики суммируются, делая колонку шире.
  3. Для рендеринга все уникальные стеки сортируются алфавитно по корневой функции — так один и тот же родитель группирует дочерние элементы рядом.
  4. Для каждого уровня прямоугольники рисуются с шириной, пропорциональной счётчику.

Эти четыре шага объясняют, почему x-ось алфавитная, а не временная: сортировка на шаге 3 группирует связанные пути, а не последовательные события. Без этого понимания каждый фрейм левее соседнего будет соблазнять тебя ложным выводом «этот выполнился раньше».

Читается сверху вниз: выбери leaf-фрейм (верх), проверь его ширину, спустись вниз, чтобы увидеть, кто его вызывает.

Самая дорогая ошибка прочтения

Позиция по x-оси — алфавитный порядок полного пути стека, не время, не порядок вызовов. Фрейм, стоящий левее другого на том же уровне, не говорит ничего о том, какой из них выполнился первым. Он говорит только о том, что путь его стека раньше по алфавиту.

Это самая распространённая ошибка инженеров при первом чтении flame graph. Если видишь два широких фрейма рядом и думаешь «сначала A, потом B», ты делаешь ложный вывод. Оба могут вызываться одним родителем в разные моменты, из несвязанных code path, из распараллеленного кода.

Если нужен порядок времени — правильный инструмент это trace view (timeline span’ов в стиле Gantt). Flame graph отвечает «что» горячее; traces отвечают «когда» в запросе выполнялся каждый шаг.

Поэтому чтение строго вертикальное: ищи самое широкое плато, потому что ширина — это число сэмплов, и широкий фрейм — это место, где CPU реально провёл время; это и есть цель оптимизации. Высокая, но узкая башня — это глубокая цепочка вызовов, которая редко бывает на CPU; это red herring, каким бы драматичным ни казался её рост. Плоская вершина широкого стека — это leaf, который реально делает работу, ведь над ним нет ничего шире.

Рабочие процессы с профилями в production

SLO burn drilldown: Срабатывает SLO alert → кликаешь ссылку → временной диапазон предфильтрован по burn-окну → CPU + off-CPU flame graph рядом → определяешь изменившуюся функцию → привязываешь к деплою. Менее 90 секунд от пейджера до git blame для любого инцидента, где баг работал на CPU.

Обнаружение регрессий деплоя: Захватываешь профиль до и после деплоя под сравнимой нагрузкой. Делаешь diff: дифференциальный flame graph (differential flame graph — граф пламени, окрашенный по изменению между двумя профилями) окрашивает фреймы по относительному изменению — красный для выросших фреймов, синий для сократившихся, белый для неизменных. Новые широкие красные фреймы, которых не было до деплоя — это регрессия. Production-grade continuous-profile бэкенды (Pyroscope, Datadog) включают это из коробки: «compare versions» выбирает два коммита или временных окна и рендерит diff.

Profile-as-data: запросы за пределами flame graph:

Профили — это временные ряды сэмплов стека; бэкенды всё чаще позволяют делать к ним запросы как к базе данных:

  • «Top-10 функций по self-CPU по всем сервисам за последний час» → capacity planning.
  • «Найти все профили, где функция X входит в top-5» → оценка влияния перед удалением медленной библиотеки.
  • «Сгруппировать flame graph’ы по Kubernetes-ноде» → найти горячие ноды.
  • «Alert при появлении новой функции в top-5 после деплоя» → автоматическое обнаружение регрессии.
Рабочий процессТриггерДействиеРезультат
SLO burn drillAlertФильтр профиля по burn-окнуГорячая функция за <90 с
Регрессия деплояДеплойDiff pre vs post профилейНовый горячий фрейм выделен красным
Capacity planningКвартальноTop-N функций по всему флотуКандидаты оптимизации в рейтинге
Trace-id drillМедленный span в traceФильтр профиля по trace-idFlame graph для этого запроса
Почему это работает

Почему дифференциальные профили находят то, что пропускают дашборды. Стандартный дашборд latency показывает рост p99 после деплоя. Но новый code path на 5% медленнее или на 50%? И какая функция изменилась? Дашборд не скажет. Дифференциальный профиль отвечает на оба вопроса: ширина красных фреймов — это серьёзность проблемы; имя фрейма и его родитель — это местонахождение. Команды, делающие автоматический diff профилей на каждый деплой, ловят регрессии за минуты, а не после жалобы клиента.

Викторина

Инженер читает flame graph и делает вывод, что функция A выполняется раньше B, потому что A стоит левее B на одном уровне. В чём ошибка?

Викторина

Только что прошёл деплой. Команда хочет узнать, было ли отклонение в CPU-производительности. Какой рабочий процесс профилирования наиболее прямой?

Вспомните перед уходом
  1. 01
    Объясни, почему x-ось flame graph алфавитная, а не временная, и какой инструмент использовать, если нужен порядок по времени.
  2. 02
    Что такое дифференциальный flame graph и какую проблему он решает?
  3. 03
    Назови три способа запрашивать профили как данные (помимо простого просмотра flame graph).
Итог

Flame graph строится агрегацией одинаковых стеков, их алфавитной сортировкой по корневой функции и отрисовкой прямоугольников с шириной, пропорциональной числу сэмплов. x-ось кодирует алфавитную группировку — никогда не время, — поэтому горизонтальная позиция фрейма не говорит ничего о порядке выполнения. Для порядка по времени используй timeline trace. Дифференциальные flame graph’ы накладывают два профиля (до и после деплоя) и окрашивают фреймы по изменению; это самый прямой способ поймать CPU-регрессии в момент деплоя. Профили — данные временного ряда: современные бэкенды поддерживают запросы по сервисам, diff по версии, группировку по ноде и alert на изменения формы, превращая profiling из инструмента отладки в непрерывный сигнал качества. Теперь, когда видишь два соседних фрейма на одном уровне и хочешь знать, какой выполнился первым, — ответ: ни один; а если порядок важен, открывай trace view.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено
Связанные уроки
встречается в170

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.