Производительность процессора видна только через счётчики PMU, Performance Monitoring Unit, и в Linux главный инструмент для этого perf. Драйвер perf_event открывает счётчики циклов, инструкций, промахов кеша и ветвлений, а события uncore показывают загрузку памяти и межсокетных линков. Ниже разобраны настройка доступа к PMU, чтение базовых метрик IPC и CPI, события для поиска горячих мест, счётчики uncore на Intel и AMD, и типичные ошибки профилирования.
Проверка PMU и настройка доступа через perf_event_paranoid
Наличие PMU видно в /proc/cpuinfo в флагах: для Intel это архитектурный PMU, для AMD IBRS и related. Утилита perf проверяет поддержку:
perf list
Вывод показывает события процессора: cycles, instructions, branches, branch-misses, cache-misses, L1-dcache-loads и другие. Число доступных счётчиков PMC обычно 4-8 на ядро для generic событий и отдельные фиксированные счётчики для cycles и instructions.
Доступ регулируется sysctl kernel.perf_event_paranoid: значение 3 запрещает всё пользовательское, 2 разрешает пользовательское без ядра, 1 разрешает и ядро, -1 разрешает всё включая трассировку:
sysctl kernel.perf_event_paranoid
sysctl -w kernel.perf_event_paranoid=1
Для замеров в контейнерах нужен доступ к /sys/kernel/debug/tracing и к MSR, что настраивается через mount debugfs и модуль msr.
Базовые метрики IPC CPI и простые события
Базовая метрика IPC, Instructions Per Cycle, показывает эффективность процессора. Значение выше 1.5 говорит о вычислительной нагрузке с хорошим использованием конвейера, значение ниже 0.5 говорит о промахах кеша или ветвлениях:
perf stat -e cycles,instructions ./programma
CPI, Cycles Per Instruction, обратная величина. perf stat с ключом -M TopdownL1 даёт иерархию узких мест: Frontend Bound, Backend Bound, Bad Speculation, Retiring. Модель Top-down на Intel показывает, где процессор теряет такты, и работает на Skylake и новее:
perf stat -M TopdownL1 ./programma
События кеша: L1-dcache-loads, L1-dcache-load-misses, LLC-loads, LLC-load-misses. Промах L2 стоит 12 тактов, промах L3 40-50 тактов, промах DRAM 200 тактов. Процент промахов L1 выше 5 процентов обычно говорит о плохой локальности данных.
События ветвлений: branch-instructions, branch-misses. Процент промахов выше 2 процентов говорит о непредсказуемых ветвлениях, решение в branchless коде или в PGO компиляции.
Поиск горячих мест через perf record и perf report
Профилирование с сэмплами:
perf record -F 99 -g ./programma
perf report --stdio
Ключ -F 99 задаёт частоту сэмплов, -g добавляет call graph. Граф вызовов собирается тремя способами: fp через frame pointer, dwarf через развёртку, lbr через Last Branch Records на Intel. Способ задаётся ключом -g, --call-graph, например -g dwarf.
Горячие функции в выводе отсортированы по проценту сэмплов. Важны self и children проценты: self показывает время в самой функции, children включает вызовы из неё. Функции libc вроде memcpy и malloc с высоким self говорят об избыточном копировании и аллокациях.
Анализ кеша точечно:
perf mem record ./programma
perf mem report
perf mem использует PEBS для точных данных о месте загрузки: L1, L2, L3, DRAM, Remote DRAM. Колонка Weight показывает время в тактах, и строки с весом выше 100 тактов обычно указывают на удалённый NUMA доступ.
События uncore на Intel и AMD
Uncore счётчики показывают работу за пределами ядер: контроллеры памяти, UPI линков, L3. На Intel события UNC_M_CAS_COUNT.RD и UNC_M_CAS_COUNT.WR показывают трафик к памяти:
perf stat -a -e UNC_M_CAS_COUNT.RD,UNC_M_CAS_COUNT.WR -- sleep 5
События UNC_CHA_DIR_LOOKUP.SNP показывают трафик когерентности на CHA, Caching and Home Agent. Для AMD события uncore именуются иначе: amd_icc, amd_df, и читаются через perf list | grep amd.
Настройка доступа к uncore требует загруженного модуля uncore_frequency или nmi_watchdog=0 для освобождения счётчиков. При конфликте с NMI watchdog perf вернёт ошибку, и её лечат через sysctl -w kernel.nmi_watchdog=0.
Типичные ошибки и диагностика профилирования
- perf_event_paranoid 3 блокирует пользовательские события, понижение до 1 или -1 решает;
- NMI watchdog занимает счётчик, perf возвращает ошибку, отключение через sysctl -w kernel.nmi_watchdog=0;
- PMC мультиплексирование искажает длинные замеры, сокращение числа событий и увеличение времени замера решают;
- -g fp без frame pointers даёт обрубленные стеки, пересборка с -fno-omit-frame-pointer или dwarf решает;
- virtualization скрывает счётчики гостя, настройка <features><pmu state='on'/></features> и passthrough PMU решает;
- container без CAP_PERFMON блокирует perf, запуск с --privileged или добавление capability решает;
- динамическое DVFS искажает замеры, фиксация частоты через cpupower frequency-set -u решает;
- SMBPBI на серверах ограничивает счётчики, обновление прошивки BMC решает.
Практический порядок анализа любого приложения: 1) perf stat с базовыми событиями, IPC и промахи кеша, 2) perf stat -M TopdownL1 для иерархии узких мест, 3) perf record -g -F 99 для стеков, 4) perf mem для локальности, 5) uncore события для трафика памяти, 6) сравнение замеров до и после оптимизации. Для CI полезен скрипт-обёртка, который запускает perf stat и проверяет пороги: IPC выше 1.0, LLC miss ниже 5 процентов, branch miss ниже 2 процентов. Падение порогов ловит регрессии производительности до попадания в прод.
Диагностика: perf list показывает события, perf stat с -M TopdownL1 даёт иерархию, perf record -g даёт стеки, perf mem даёт локальность данных, perf stat -a с uncore событиями даёт трафик памяти. Связка этих четырёх инструментов покрывает 90 процентов задач анализа производительности CPU.
Порядок анализа любого приложения: 1) perf stat с базовыми событиями, IPC и промахи кеша, 2) perf stat -M TopdownL1 для иерархии узких мест, 3) perf record -g -F 99 для стеков, 4) perf mem для локальности, 5) uncore события для трафика памяти, 6) сравнение замеров до и после оптимизации.
Для CI полезен скрипт-обёртка, который запускает perf stat и проверяет пороги: IPC выше 1.0, LLC miss ниже 5 процентов, branch miss ниже 2 процентов. Падение порогов ловит регрессии производительности до попадания в прод.
Выводы: perf с PMU счётчиками даёт точную картину работы процессора, и правильная настройка доступа через perf_event_paranoid и nmi_watchdog открывает полный набор событий. Top-down анализ на Intel и IPC на AMD быстро указывают класс узкого места: вычисления, память или ветвления. Один вечер освоения perf экономит недели профилирования printf-ом.
Полная иерархия Top-down на Intel уровня L1 делит такты на четыре категории: Retiring полезная работа, Bad Speculation потраченные такты на спекуляцию и промахи предсказания, Frontend Bound нехватка инструкций из выборки, Backend Bound нехватка ресурсов исполнения. Уровень L2 раскладывает каждую категорию детальнее: Backend Bound на Memory Bound и Core Bound, Frontend Bound на Fetch Latency и Fetch Bandwidth.
Замер уровня L2 на горячей функции:
perf stat -M TopdownL2 ./programma -- hot_function
Значение Memory Bound выше 30 процентов говорит о работе с памятью, и дальше смотрят через perf mem. Core Bound выше 30 процентов говорит о нехватке исполнительных устройств, и помогает распараллеливание или векторизация. Bad Speculation выше 15 процентов говорит о непредсказуемых ветвлениях, и помогает PGO или branchless код.
На AMD модель Top-down недоступна, и узкое место ищут через пары событий: cycles вместе с LS Not Stalled показывает вычислительную нагрузку, cycles с icache miss показывает проблему выборки, cycles с L2/L3 miss показывает проблему памяти. Аналогичный вывод получают через amd uProf, коммерческий аналог perf от AMD с поддержкой Zen-специфичных событий.
Topdown анализ, контейнеры и кластеры
В контейнерах perf требует CAP_PERFMON и CAP_SYS_ADMIN, иначе вернёт ошибку Operation not permitted. Запуск контейнера с нужными capabilities:
docker run --cap-add=PERFMON --cap-add=SYS_ADMIN ...
Альтернатива, доступ к host PMU через монтирование /sys/bus/event_source/devices и запуск perf с --cgroup для фильтрации по контейнеру. Kubernetes настраивает через securityContext с capabilities в манифесте пода.
На кластерах perf работает на каждом узле отдельно, и для агрегации используются flame graphs через FlameGraph скрипты от Brendan Gregg. Связка perf record -g с flamegraph.pl даёт интерактивную картину по всему кластеру, собранную в один SVG.
Для непрерывного мониторинга perf в проде используют выборочное профилирование: раз в час записывают 30 секунд сэмплов на узле и отправляют в централизованный сбор через Parca или Pyroscope. Такой continuous profiling ловит регрессии производительности в течение дня, а не после жалоб.
Виртуализация скрывает PMU от гостя по умолчанию. Настройка libvirt через features pmu state on включает виртуальный PMU, и в госте perf работает с ограниченным набором событий. Полный passthrough PMU требует QEMU параметра -cpu host,+pmu и поддержки со стороны ядра гипервизора.
Замеры в госте с виртуальным PMU: cycles и instructions работают, uncore недоступен. Это значит, что в госте видно IPC и промахи кеша ядра, но не трафик памяти хоста. Для полного анализа на госте запускают приложение, а uncore читают на хосте через perf stat -a в это же время.
Контейнеры с namespace изоляцией видят только свои события, и perf --cgroup фильтрует по cgroup. Это позволяет профилировать конкретный контейнер без шума от соседей, что критично на многопользовательских узлах.
Оптимизация сборки с PMU данными и профили приложений
PGO, Profile-Guided Optimization, использует данные профилирования для оптимизации раскладки кода и предсказания ветвлений. Сбор профиля: gcc -fprofile-generate, запуск нагрузки, gcc -fprofile-use. Выигрыш 5-15 процентов на ветвистом коде без изменения исходников.
LTO, Link Time Optimization, в сочетании с PGO даёт ещё 3-7 процентов за счёт инлайнинга через границы единиц компиляции. Для горячих библиотек like OpenSSL или glibc сборка с PGO и LTO стандартная практика в дистрибутивах.
BOLT, Binary Optimization and Layout Tool, работает на уже собранном бинарнике с профилем perf data: перекомпонует функции и базовые блоки по частоте исполнения. Выигрыш 5-20 процентов на больших бинарниках без пересборки, что критично для проприетарного кода.
Базы данных PostgreSQL: топ функций обычно heap_tuple_fetch и buffer management, и узкое место чаще в памяти, чем в вычислениях. IPC обычно 0.8-1.2, Memory Bound высокая, и тюнинг направлен на shared_buffers и индексы.
Веб-серверы Nginx и Envoy: узкое место в системных вызовах и копировании, IPC 1.5-2.5, и оффлоады через sendfile и TCP Zerocopy снижают нагрузку. Профиль показывает epoll_wait и skb_copy, и решение в io_uring или XDP.
Научные коды и ML: узкое место в матричных вычислениях, IPC 2-4 с AVX, и оптимизация направлена на векторизацию и NUMA. Профиль показывает FMA инструкции и NUMA remote access, решение в libblis или oneDNN с NUMA-привязкой.
Хранилища и файловые системы: узкое место в I/O, IPC ниже 0.5 с высокими llc miss, и решение в o_direct, aio или io_uring с соответствующей очередью.
Perf с PMU даёт точную и безопасную картину производительности, и настройка доступа через perf_event_paranoid и nmi_watchdog открывает полный набор событий. Top-down анализ на Intel быстро указывает класс узкого места, IPC и промахи кеша дают базу для сравнения версий.
Порядок работы с любым приложением: базовый perf stat, Top-down, стеки через perf record -g, локальность через perf mem, uncore для памяти, и сравнение до и после оптимизации. Для CI добавляют пороги на IPC и промахи кеша, для кластеров continuous profiling через Parca или Pyroscope.
Вывод: один вечер освоения perf и PMU счётчиков экономит недели профилирования printf-ом, и превращает оптимизацию из угадывания в инженерию с измеримыми метриками.
Ключевое правило работы с perf: один инструмент на один вопрос. IPC отвечает на вопрос "эффективно ли работает процессор", Top-down на вопрос "где теряются такты", perf record на вопрос "какая функция горячая", perf mem на вопрос "где живут данные", uncore на вопрос "не упёрлись ли в память". Смешивать вопросы в одном замере бессмысленно: мультиплексирование счётчиков искажает цифры, и каждый вопрос решается отдельным прогоном.
Инструменты вокруг perf: FlameGraph для визуализации стеков, BCC и bpftrace для динамических трассировок без перезапуска, Parca и Pyroscope для непрерывного профилирования в проде. Связка perf с этими инструментами покрывает весь цикл от разработки до продакшена, и превращает анализ производительности в регулярную инженерную практику, а не в разовую аварию.