В блоге Ubuntu Server вышел разбор Profile-Guided Optimization (PGO) на реальном стеке: эмуляция RISC-V в QEMU, на которой крутятся сборки пакетов в Launchpad. Не абстрактный «ускорим всё», а замеры на четырёх пакетах, с цифрами по CPU и времени сборки. Ниже – что такое PGO, как измеряли и какие выводы сделали.
Что такое PGO
Profile-Guided Optimization (ещё называют FDO, feedback-driven optimization) – схема из двух (и больше) проходов компиляции. Сначала бинарник собирают так, чтобы снять профиль реального выполнения. Потом компилятор (в кейсе Ubuntu – GCC) использует этот профиль и сильнее оптимизирует «горячие» ветки: раскладку кода, инлайнинг, предсказание ветвлений.
Классическая инструментация внутри бинарника тормозит прод, поэтому в исследовании опирались на внешний sampling через perf (PMU ядра Linux) и конвертацию данных в формат, понятный GCC, через пакет autofdo. Так профиль ближе к боевой нагрузке, без жёсткого штрафа от полной instrumented-сборки.
Важный нюанс: PGO заточен под тот сценарий, который профилировали. На совсем другой нагрузке выигрыш может исчезнуть или даже уйти в минус. Поэтому в кейсе отдельно проверяли, как QEMU, оптимизированный по профилю одной сборки, ведёт себя на других пакетах.
Зачем смотрели именно QEMU и RISC-V
Ubuntu давно собирает пакеты под RISC-V. Когда «железа» не хватало, builders в Launchpad подняли как QEMU-эмуляцию RISC-V поверх AMD64. Эти эмулированные builders до сих пор делают сотни сборок в день. Для Server team это идеальный кандидат: тяжёлая, повторяемая, узкоспециализированная нагрузка – как раз то, где PGO обычно даёт максимум.
Как собирали профиль и нагрузку
Профиль снимали с QEMU во время сборки пакетов внутри гостевой RISC-V среды. GCC сам по себе не ест raw-файлы perf, поэтому цепочка: perf record → конвертация через autofdo → gcov-подобный вход для повторной сборки QEMU с PGO.
Критерии пакетов для нагрузки:
- сборка на RISC-V builders разумной длины (ориентир: больше часа, но не «на весь день»);
- разный стек языков/фреймворков, чтобы проверить перенос оптимизации с одной нагрузки на соседние.
Итоговый набор:
- OpenSSL (C) – по нему снимали профиль и собирали PGO-QEMU;
- GDB (C++);
- Emacs (C + много Elisp);
- Python 3.12 (C + много Python-кода в сборке).
Сначала мерили «обычный» QEMU на всех четырёх, затем – QEMU, собранный с PGO по профилю OpenSSL. Для статистики – средние по 5 прогонам на пакет.
Железо и параметры съёмки
Тестовый стенд из статьи: AMD Ryzen 9 7950X3D (16 ядер), 128 GB DDR5 ECC, SSD Gen4. QEMU для RISC-V virt с 8 vCPU и 8 GB RAM, без дисплея, с OpenSBI/U-Boot. perf писали с ветвлениями (branch_insn / branch-any), sampling пачками: 5 минут записи, пауза, снова запись – из-за размера perfdata (иначе легко вылезти за десятки гигабайт).
perf record \
-e branch_insn \
--branch-any \
-p ${QEMU_PID} \
--all-cpus \
--count 100003 \
--quiet \
--output output.perfdata \
-- sleep 300
Результаты
Смотрели среднее использование CPU (в минутах) и среднее время сборки. Везде PGO-QEMU выигрывал примерно в одном коридоре 5-7%.
- OpenSSL: CPU примерно -5.3%, время сборки около -5.5% (порядка 2 минут). Ожидаемо: профиль как раз с этой нагрузки.
- GDB: CPU около -5.5%, время около -5.7% (порядка 4 минут). C++-сборка тяжелее линковкой, но выигрыш удержался.
- Emacs: один из лучших результатов – CPU около -5.7%, время около -6.7% (порядка 5.5 минут).
- Python 3.12: CPU около -5.2%, время около -6.3% (порядка 7.5 минут).
Интересная деталь по OpenSSL: процент загрузки CPU в моменте мог быть чуть выше, но из-за более короткой сборки суммарные CPU-минуты всё равно падали. На масштабе Launchpad (много сборок в сутки) даже 5% времени – это часы машинного времени и потенциально больше параллельных задач.
Что из этого следует
PGO здесь сработал: и CPU, и wall-clock улучшились на похожих, но не идентичных нагрузках (разные языки, один класс работы – сборка пакета в эмуляторе). Это хороший аргумент для узких, повторяемых сценариев: CI builders, эмуляция, сервисы с предсказуемым hot path.
При этом авторы прямо пишут: PGO не серебряная пуля. Чем шире и разнообразнее workload, тем слабее эффект; на «чужой» нагрузке профиль может даже навредить. Если код огромный и руками через perf top / perf record сложно найти рычаг, PGO – разумный автоматический слой. Если нагрузка пляшет каждый день – сначала стоит проверить, есть ли вообще стабильный профиль.
Для пользователей дистрибутива
Этот пост Canonical – case study по QEMU для builders, а не анонс «PGO включили во все пакеты Ubuntu». Пользователь desktop/server не обязан ничего пересобирать: смысл материала в методологии и в том, куда имеет смысл вкладывать инженерное время. Для тех, кто сам гоняет тяжёлую эмуляцию или кастомные пакеты, цепочка perf + AutoFDO + GCC PGO описана и в документации Ubuntu Server.
Краткий итог
Ubuntu Server team показала на практике: PGO на QEMU (профиль с OpenSSL, проверка на GDB/Emacs/Python) даёт устойчивые ~5-7% по CPU и времени сборки в эмуляции RISC-V. Техника сильна на специализированных нагрузках и требует честного профилирования, а не «включи флаг и забудь». Для дистрибутива и CI это реальный рычаг; как единственную стратегию оптимизации всего подряд её рассматривать не стоит.


