Частоты, троттлинг и NUMA: turbostat и numastat

Рейтинг: 62.1% · 15 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Частоты, троттлинг и NUMA: turbostat и numastat

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat (вы здесь)
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
Бывает так: сервер вроде не загружен под завязку, CPU показывает 40% в top, а приложение тормозит и latency скачет. Ты смотришь load average, диск, сеть - всё чисто. А разгадка прячется этажом ниже, в железе: ядра не разгоняются до своей реальной частоты, упёрлись в тепловой троттлинг или процесс бегает по чужой памяти через шину между сокетами. Эти штуки top тебе не покажет, потому что для него такт - это такт, медленный он или быстрый. В этом уроке разберёмся, как поймать просадку частоты CPU в Linux и проблемы NUMA руками, без гадания.

Тема считается "для продвинутых", но на серверах с базами данных, тяжёлой in-memory логикой и двумя сокетами она решает. Начнём с азов и дойдём до perf и eBPF. Всё, что ниже, актуально на 2026 год: на современных дистрибутивах (свежие Debian/Ubuntu, RHEL 9/10, Astra Linux, RED OS) по умолчанию cgroup v2, ядро 6.x, а eBPF уже зрелый и местами честно вытеснил старые подходы.

Откуда берётся частота CPU в Linux и почему она не на максимуме

Современное ядро процессора не работает на одной фиксированной частоте. Оно постоянно её меняет: под нагрузкой разгоняется в turbo (выше базовой), без работы засыпает в энергосберегающие состояния. За это отвечают три вещи: драйвер масштабирования, governor поверх него и idle-состояния.

Драйвер масштабирования частоты. На современных серверах это почти всегда intel_pstate (Intel) или amd-pstate (AMD, активно используется с ядра 5.17+ и зрелый к 2026). Важный нюанс: у intel_pstate в режиме active доступны только два governor - performance и powersave, и его powersave - это не "держим минимальную частоту", а аппаратный аналог ondemand, частота скачет по нагрузке. У amd-pstate в режиме active дефолтный governor - schedutil. Это первое, что путает новичков: "стоит powersave" на Intel не всегда значит "зажали в пол", надо смотреть фактические МГц, а не имя.

Governor (регулятор частоты). Это политика, по которой ядро решает, на какой частоте крутиться:
  • performance - держим частоту высокой, экономия не волнует. То, что нужно latency-чувствительному серверу.
  • powersave - экономим. На старом acpi-cpufreq это буквально минимальная частота; на intel_pstate active - подстройка под нагрузку. Классика "почему cpu частота в Linux низкая".
  • schedutil - современный governor, частоту считает сам планировщик ядра по нагрузке. Дефолт на многих дистрибутивах 2026 и на amd-pstate.
  • ondemand/conservative - старые governor для acpi-cpufreq, ещё встречаются на бюджетных и старых машинах.
C-states (idle-состояния). Когда ядру нечего делать, оно засыпает. C0 - активная работа, C1/C1E/C6 и глубже - сон разной глубины. Чем глубже сон, тем больше экономии, но тем дольше просыпаться (exit latency растёт от наносекунд в C1 до десятков микросекунд в C6). Для latency-чувствительных задач глубокий сон - это лишние микросекунды на каждый "проснись и работай", поэтому под трейдинг и низколатентные сервисы C-states иногда ограничивают через ядерный параметр intel_idle.max_cstate или processor.max_cstate.

Тепловой троттлинг (cpu throttling в Linux). Если процессор перегревается (плохое охлаждение, забитый пылью радиатор, тесный корпус, сдохший вентилятор), он принудительно сбрасывает частоту, чтобы не сгореть. Снаружи это выглядит как "сервер ни с того ни с сего просел". Отдельно есть power limit (RAPL): сокет упирается в лимит по ваттам и тоже не разгоняется, даже если по теплу запас есть. Аналогия: бегун в жару переходит на шаг не потому что устал, а чтобы не получить тепловой удар.

Изображение

turbostat: смотрим реальные частоты, C-states и троттлинг

turbostat - утилита из пакета linux-tools / linux-cpupower (Debian/Ubuntu) или kernel-tools (RHEL/Fedora/Astra/RED OS). Она показывает то, что governor реально делает с железом: фактические частоты по ядрам, residency по C-states и потребление в ваттах через MSR-регистры процессора. Требует root.

Код: Выделить всё

sudo turbostat --interval 5
Кусок вывода (сокращён, реальный шире):

Код: Выделить всё

Core  CPU  Avg_MHz  Busy%  Bzy_MHz  TSC_MHz  IRQ  POLL  C1   C6    CoreTmp  PkgWatt
   -    -      1356  42.10     3210     2400  ...                       61    78.50
   0    0      3431  95.30     3600     2400  ...                       63
   0    1       148   5.10     2900     2400  ...                       63
   1    2      3149  88.70     3550     2400  ...                       60
Как это читать (это сердце урока):
  • Avg_MHz - средняя частота за весь интервал, включая простой. Считается как реально отработанные такты делить на длину интервала. Низкий Avg_MHz при низком Busy% - это просто простой, не паникуй.
  • Busy% - сколько процентов интервала ядро реально считало (было в C0). 95% - ядро пахало почти всё время.
  • Bzy_MHz - средняя частота именно в момент работы, без учёта простоя (Avg_MHz делить на Busy%). Вот это ключевая цифра. Если в спеке CPU базовая 2400 и turbo до 3600, а Bzy_MHz под нагрузкой болтается около 2000 - ядро не разгоняется, ищи причину (powersave/минимальная частота или троттлинг).
  • TSC_MHz - опорная частота таймера, фиксированная, обычно близка к базовой. Сравнивай Bzy_MHz с ней: Bzy сильно ниже TSC при высоком Busy% - тревога, недоразгон.
  • C1/C6 - процент времени в соответствующем idle-состоянии (residency). Высокий C6 на ядре, которое должно работать, - либо ядро реально простаивает, либо latency теряется на выходе из сна.
  • CoreTmp - температура ядра в градусах. Под нагрузкой 60-75 это норм, 90+ и близко к Tjmax (часто 100) - почти наверняка тепловой троттлинг.
  • PkgWatt - сколько ватт ест весь пакет (сокет). Упёрлось в потолок (power limit/RAPL) - тоже причина не разгоняться.
Хочешь прицельно проверить именно троттлинг - добавь счётчики через --add или смотри флаги в --debug. Современный turbostat умеет колонки CoreThr (процент времени в тепловом троттлинге ядра) и поля по причинам ограничения частоты. Если CoreThr ненулевой - вопрос закрыт, греется.

Если видишь высокий Busy%, но низкий Bzy_MHz - проверь драйвер и governor:

Код: Выделить всё

cpupower frequency-info
В выводе ищи строку "driver:" (intel_pstate / amd-pstate / acpi-cpufreq), "current policy" и "The governor ... ". Видишь powersave на боевом сервере с acpi-cpufreq - меняем на performance:

Код: Выделить всё

sudo cpupower frequency-set -g performance
Проверить руками без cpupower можно прямо через sysfs:

Код: Выделить всё

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
На Astra Linux и RED OS всё то же самое: пакеты называются как в их базе (kernel-tools / linux-tools), пути в /sys одинаковые - это часть ядра, а не дистрибутива.

NUMA: почему чужая память медленнее и при чём тут numactl

На серверах с двумя и более процессорными сокетами память физически поделена между ними. Это NUMA (Non-Uniform Memory Access). Каждый сокет (нода) имеет "свою" локальную память. Доступ к своей памяти быстрый. Доступ к памяти соседнего сокета идёт через межпроцессорную шину (UPI у Intel, Infinity Fabric у AMD) и медленнее - иногда в полтора-два раза по latency. На больших AMD EPYC NUMA-ноды бывают даже внутри одного сокета (режим NPS), так что NUMA - это не всегда "два физических процессора". Аналогия: своя книга на твоём столе против книги, за которой надо идти к коллеге в другую комнату.

Когда это важно: базы данных (PostgreSQL, MySQL, ClickHouse), Redis с большим датасетом, HPC, JVM с огромной кучей, Kafka. Если процесс крутится на ноде 0, а его память легла на ноду 1 - каждый промах кэша в последнем уровне (LLC) бьёт по дальней памяти, и latency плавает.

Посмотреть топологию нод:

Код: Выделить всё

numactl --hardware

Код: Выделить всё

available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 32094 MB
node 0 free: 1208 MB
node 1 cpus: 8 9 10 11 12 13 14 15
node 1 size: 32157 MB
node 1 free: 25431 MB
node distances:
node   0   1
  0:  10  21
  1:  21  10
Тут видно две ноды. Обрати внимание на "node distances": 10 - стоимость доступа к своей памяти, 21 - к чужой (это относительные единицы из ACPI SLIT, не наносекунды). Чем больше разрыв, тем дороже промахи на чужую ноду. И ещё звоночек: на ноде 0 свободно всего 1.2 ГБ, а на ноде 1 - 25 ГБ. Перекос - значит кто-то на ноде 0 уже лезет за памятью к соседу.

numastat: ловим промахи нод

numastat показывает счётчики аллокаций памяти по нодам в страницах. Запускаем без аргументов - общесистемная картина:

Код: Выделить всё

numastat

Код: Выделить всё

                           node0           node1
numa_hit              184523011        90233412
numa_miss               2049883            10422
numa_foreign              10422         2049883
interleave_hit             8801            8790
local_node            183001244        90100021
other_node              1521767           133391
Что значат поля (тут новички чаще всего путаются, читай внимательно):
  • numa_hit - память успешно выделена на той ноде, которую процесс и предпочитал. Это хорошо, таких должно быть подавляющее большинство.
  • numa_miss - память выделена на ЭТОЙ ноде, хотя процесс предпочитал ДРУГУЮ (на той не нашлось свободных страниц). То есть miss считается на ноде-получателе, куда память "приземлилась" вынужденно.
  • numa_foreign - зеркало miss: память предназначалась ЭТОЙ ноде, но ушла на другую. Каждому numa_miss на одной ноде соответствует numa_foreign на ноде, которая изначально была предпочтительной (посмотри на цифры выше - 2049883 и 10422 переставлены местами между нодами, это и есть пара miss/foreign).
  • interleave_hit - страницы, выделенные по политике interleave (равномерное чередование по нодам) и попавшие на эту ноду как и планировалось.
  • local_node - память выделена процессу, который бежал на этой же ноде. Чистая локальность, идеал.
  • other_node - память выделена на этой ноде, но процесс в этот момент бежал на ДРУГОЙ ноде. Тоже признак, что планировщик и память разъехались.
Не путай numa_miss и other_node: miss/foreign - про "хотели ноду X, не влезли, легли на Y"; local_node/other_node - про "где бежал процесс относительно того, откуда взял память". Норма - numa_miss и other_node малы на фоне огромного numa_hit и local_node. Если miss или other_node растут на тысячи в секунду - это и есть твоя плавающая latency.

Лечится привязкой процесса к ноде через numactl. Запустить так, чтобы и CPU, и память жили на ноде 0:

Код: Выделить всё

numactl --cpunodebind=0 --membind=0 ./my_server
Многие СУБД умеют это сами: у MySQL есть innodb_numa_interleave, PostgreSQL под высокой памятью часто запускают как numactl --interleave=all, чтобы размазать память равномерно и не упереться в одну ноду. Посмотреть статистику по конкретному процессу (в мегабайтах по нодам):

Код: Выделить всё

numastat -p $(pgrep -n postgres)
В 2026 у ядра есть и автоматический балансировщик NUMA (sysctl kernel.numa_balancing): он сам мигрирует страницы к ядрам, которые к ним обращаются. Иногда он помогает, иногда мешает БД с собственной привязкой - тогда его отключают (kernel.numa_balancing=0). Проверь, включён ли он, прежде чем винить чистый numactl.

perf stat: IPC и cache-misses - доказательство деградации

Частоты и NUMA в итоге упираются в одну метрику эффективности - IPC (instructions per cycle, инструкций за такт). Снимаем её через perf stat (пакет linux-tools-common / perf):

Код: Выделить всё

sudo perf stat -a sleep 10

Код: Выделить всё

   142819,33 msec task-clock        #   14,2 CPUs utilized
        9 832   context-switches    #   68,8 /sec
          412   cpu-migrations      #    2,8 /sec
  301 442 118   cache-misses        #   28,90% of all cache refs
  1,043e12       cycles             #    3,21 GHz
  4,118e11       instructions       #    0,39  insn per cycle
Как читать:
  • insn per cycle (IPC) - сколько инструкций ядро успевает за такт. 0,39 - это очень мало, ядро простаивает в ожидании памяти. Здоровый счётный код держит 1.0-2.0+ (а векторный и выше). Низкий IPC при высоком Busy% - классика memory-bound нагрузки, и часто это именно дальняя NUMA-память или промахи кэша.
  • cache-misses ... % of all cache refs - доля промахов кэша. 28% - высоковато; чем больше промахов, тем чаще лезем в медленную RAM. Само по себе это не диагноз, но в связке с низким IPC - сильный сигнал.
  • cpu-migrations - сколько раз процессы перепрыгнули между ядрами. Каждый прыжок обнуляет тёплый кэш ядра, а на NUMA ещё и может увести процесс от его памяти. Много миграций - повод привязать процесс (taskset/numactl/cgroup cpuset).
  • cycles / GHz - тут же видишь реальную частоту. 3,21 GHz - turbo работает; если бы тут было 2,0 - вернулись бы к turbostat искать недоразгон.
Хочешь сразу разложить нагрузку на компоненты - возьми высокоуровневую методику Top-down: perf stat --topdown (или perf stat -M TopdownL1). Она прямо скажет, во что упёрлось ядро: Frontend Bound, Backend Bound (часто = память), Bad Speculation или Retiring. Это намного быстрее, чем гадать по отдельным счётчикам, и в 2026 это стандартный первый шаг для memory-bound разбора.

eBPF в 2026: когда он лучше perf и strace

Перфоманс-стек повзрослел, и многое теперь делается через eBPF (ядро 5.x+, на 2026 это норма). Где он реально заменил старое:
  • Профилирование вместо ручного perf record: bcc-инструмент profile снимает стек-сэмплы дёшево и сразу строит флеймграф; для разовых вопросов хватает bpftrace-однострочника.
  • llcstat (bcc) - сэмплит обращения к кэшу последнего уровня и показывает hit/miss по процессам. Это тот же сигнал, что cache-misses в perf stat, но с разбивкой по PID без отдельного парсинга.
  • perf c2c (cache-to-cache) - отдельно стоит знать: ловит false sharing и межсокетный пинг-понг строк кэша, прямой инструмент против NUMA-деградации.
  • strace уходит на второй план: для трассировки syscall в продакшене strace тормозит процесс в разы (ptrace), а bpftrace и bcc (trace, syscount, funccount) делают то же почти без накладных расходов и без остановки процесса. strace оставляем для интерактивной отладки одного процесса, eBPF - для боевого сервера.
Связка простая: turbostat говорит, на какой частоте крутимся; numastat - не лезем ли в чужую память; perf stat / Top-down показывает итог в IPC; eBPF (profile, llcstat, perf c2c) добавляет адресность по процессам и строкам кэша.

Типичные грабли и заблуждения
  • "CPU 40% в top, значит запас есть". Нет. top не видит ни просевшей частоты, ни промахов NUMA. 40% медленных тактов хуже 40% быстрых.
  • "Стоит powersave, всё пропало". На intel_pstate active powersave - это не минимальная частота, а подстройка по нагрузке. Не верь имени, смотри Bzy_MHz в turbostat.
  • Governor performance не помог. Проверь питание (PkgWatt у потолка, RAPL) и температуру (CoreTmp, CoreThr). Если троттлит по теплу или по power limit - governor бессилен, чисти охлаждение или поднимай лимит.
  • Привязал numactl --membind на ноду, где мало free. Получишь OOM или принудительный miss. Сначала numactl --hardware, смотри free по нодам.
  • Винишь numactl, а это автобалансировщик. kernel.numa_balancing может тасовать страницы и греть other_node. Для БД с ручной привязкой его часто отключают.
  • turbostat пишет "no /dev/cpu/0/msr". Нужен модуль: sudo modprobe msr. В виртуалках turbostat часто не видит частоты вообще - MSR не проброшены, это нормально (та же причина, почему частоты в облачной VM смотреть бессмысленно).
  • Путаешь Avg_MHz и Bzy_MHz. Avg_MHz усреднён по всему интервалу (включая сон), Bzy_MHz - только по времени работы. Для оценки разгона смотри Bzy_MHz.
Мини-лаба: повтори руками прямо сейчас
  • Запусти sudo turbostat --interval 5, нагрузи систему (например stress-ng --cpu 0 или yes > /dev/null в несколько потоков по числу ядер) и смотри, как растут Busy% и Bzy_MHz, и держится ли CoreTmp ниже опасного.
  • Посмотри драйвер и governor: cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver и scaling_governor. Если acpi-cpufreq и есть права на тестовой машине - переключи на performance через cpupower и сравни Bzy_MHz.
  • Выполни numactl --hardware. Если нод две и больше - запусти numastat и оцени numa_miss и other_node относительно numa_hit. Глянь kernel.numa_balancing через sysctl.
  • Сними sudo perf stat -a sleep 10 в покое и под нагрузкой, сравни insn per cycle. Если есть perf свежий - попробуй perf stat -M TopdownL1 и посмотри, Backend ли Bound.
Контрольные вопросы
  • Какая колонка turbostat показывает реальную частоту ядра в момент работы и с чем её сравнивать, чтобы поймать недоразгон?
  • Чем numa_miss отличается от other_node, и почему рост numa_miss - это плохо?
  • Что означает низкий IPC (insn per cycle) при высоком Busy%, и какой инструмент даст разложение по Frontend/Backend Bound?
  • Governor стоит performance, но частота всё равно низкая - какие метрики turbostat проверить и почему performance тут может быть бессилен?
Что запомнить

top показывает загрузку, но не показывает эффективность. turbostat ловит просадку частоты, C-states и тепловой троттлинг (смотри Bzy_MHz, CoreTmp, PkgWatt, CoreThr), но помни про intel_pstate/amd-pstate - имя governor обманчиво. numactl --hardware и numastat вскрывают проблему дальней памяти на многосокетных серверах (следи за numa_miss и other_node), а kernel.numa_balancing может всё подпортить. perf stat сводит всё к одной честной цифре - IPC, а perf stat --topdown сразу скажет, упёрлись ли в память. В 2026 рядом стоят eBPF-инструменты (profile, llcstat, perf c2c), которые делают то же дешевле и адреснее strace и ручного perf record. Governor/драйвер в энергосбережении и перегрев - две самых частых причины, почему cpu частота в Linux не идёт в turbo. На обычном одно-сокетном сервере про NUMA можешь почти не вспоминать; на базах данных и тяжёлых машинах с двумя сокетами - это первое, что стоит проверить.
👍7 ❤️4 🔥 😄 🤔1
Аватара пользователя
pro_petr
Сообщения: 1
Зарегистрирован: 16 май 2026, 16:57

Re: Частоты, троттлинг и NUMA: turbostat и numastat

Сообщение pro_petr »

Блин, два дня искал почему postgres дёргается на двухсокетном серваке, а это numa_miss пёр тысячами и other_node рос. numactl --membind=0 плюс выключил numa_balancing, и latency выровнялась. В закладки.
👍1 ❤️ 🔥 😄 🤔
Аватара пользователя
zfsnerd
Сообщения: 1
Зарегистрирован: 13 май 2026, 14:21

Re: Частоты, троттлинг и NUMA: turbostat и numastat

Сообщение zfsnerd »

Подскажите, а в виртуалке turbostat вообще должен работать? У меня пишет no /dev/cpu/0/msr и пустые частоты, modprobe msr не помог. Это потому что хост MSR не пробрасывает и в облаке частоты смотреть смысла нет?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Профилирование CPU: perf top и поиск пожирателя
Следующая глава →
Память Linux: RSS, VSZ, page cache и миф о нехватке памяти

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость