habrahabr

Проценты использования процессора — это ложная метрика

  • воскресенье, 7 сентября 2025 г. в 00:00:10
https://habr.com/ru/articles/944172/

По работе я постоянно имею дело с серверами; при этом их владельцы всегда хотят знать, когда серверы используют свои ресурсы максимально. Вроде бы, это простая задача? Достаточно настроить top или другой инструмент мониторинга системы, посмотреть на процент использования сети, памяти и CPU, и наибольшее значение покажет, насколько близко сервер находится к пределу своих возможностей.

A screenshot of a system monitor app showing 24 cores, half of which are at 100% utitilization and half of which are close to 0%.
Например, эта машина потребляет 50% ресурсов CPU, поэтому, вероятно, способна выполнять вдвое больше своих задач.

Однако когда владельцы пытаются реально проецировать эти значения, то оказывается, что процент использования CPU на самом деле растёт не совсем линейно. Но насколько непрямой может быть зависимость?

Чтобы ответить на этот вопрос, я выполнил кучу стресс-тестов, мониторя при этом объём выполняемых ими работы и отображаемый системой уровень использования CPU, а затем по результатам построил графики.

Подготовка

В качестве тестовой машины я использовал десктоп Ubuntu с процессором Ryzen 9 5900X (12 ядер / 24 потока). Также я включил Precision Boost Overdrive (то есть Turbo).

При помощи вайб-кодинга я написал скрипт, выполняющий в цикле stress-ng, сначала с использованием 24 воркеров, попробовав выполнять их каждый при разных уровнях нагрузки на CPU, от 1% до 100%, а затем использовал от 1 до 24 при нагрузке 100%. Тест использовал разные методики нагрузочного тестирования и измерял количество операций, которые можно было выполнить («Bogo ops1»).

Я проводил две методики тестирования, потому что операционные системы умно используют свой планировщик, поэтому планирование небольшого количества воркеров с использованием процессора на 100% можно реализовать оптимально, но когда все 24 воркера находятся на уровне использования 50%, операционной системе сложно сделать что-то иное, нежели распределить работу равномерно.

Результаты

Сырые результаты в CSV можно посмотреть здесь: https://docs.google.com/spreadsheets/d/1QKdYa3NIFGTixG_LdnsbwbeLDnE2GNVVsS9-dfkcT20/edit?usp=sharing.

Общий тест CPU

Самый простой тест просто выполняет в цикле все стресс-тесты CPU утилиты stress-ng.

Как мы видим, когда система сообщает об использовании CPU на 50%, на самом деле он выполняет 60-65% от реальной максимальной работы.

64-битная целочисленная математика

Хм, возможно, это была случайность? Что, если просто выполнять какие-то произвольные вычисления с 64-битными integer?

Здесь картина ещё печальнее! При «использовании на 50%» мы на самом деле выполняем 65-85% от максимальной работы. Ну, хуже ведь уже быть не может?

Матричные вычисления

Что-то тут определённо не так. При выполнении матричных вычислений «использование на 50%» — это, на самом деле, от 80% до 100% максимально возможного объёма работы.

На скриншоте с монитором системы из начала статьи показан тест матричных вычислений с 12 воркерами; можно увидеть, что он сообщал об использовании на 50%, несмотря на то, что дополнительные воркеры абсолютно ничего не делают (за исключением повышения значений использования ресурсов CPU).

Бонус: Nginx

На Hacker News мне предложили провести реальный бенчмарк, поэтому я прогнал бенчмарк Nginx из Phoronix Test Suite, ограниченный 1-24 ядрами (к сожалению, я не мог более точно контролировать процент использования CPU, поэтому получился только один график).

Здесь мы видим, что изначально определяемый процент использования ниже реального, а затем ситуация ухудшается. При использовании на 50% на самом деле выполняется 80% от максимального количества запросов в секунду, а при 80% мы на самом деле уже на 100% от максимального объёма запросов.

Что происходит?

Hyperthreading

Можно заметить, что график меняется на 50%, поэтому я добавил кусочно-линейные регрессии, показывающие выравнивание.

Главная причина происходящего — hyperthreading: половина «ядер» на этой машине (и на большинстве машин) делит ресурсы с другими ядрами. Если я запущу на этой машине 12 воркеров, то каждому из них назначат отдельное физическое ядро без общих ресурсов, но при превышении их количества каждый дополнительный воркер будет делить ресурсы с другим. В некоторых случаях (общие бенчмарки CPU) это лишь немного ухудшает ситуацию, а в других (матричные вычисления с активным применением SIMD) не остаётся ресурсов, которые можно делить.

Turbo

Это сложнее заметить, но Turbo тоже влияет на результаты. При низком уровне использования этот конкретный процессор работает на частоте 4,9 ГГц, но с увеличением количества активных ядер частота медленно снижается до 4,3 ГГц2.

Посмотрите на ось Y. На этом процессоре тактовая частота падает «всего» на 15%.

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

Важно ли это?

Если вы смотрите на уровень использования CPU и предполагаете, что он будет возрастать линейно, то у вас возникнут проблемы. Если вы используете CPU эффективно (показатель использования выше «50%»), то отображаемый уровень использования на самом деле преуменьшен, и иногда существенно.

Имейте также в виду, что я показал результаты только для одного процессора, но производительность hyperthreading и поведение Turbo могут сильно варьироваться для разных моделей процессоров, в особенности от разных производителей (AMD и Intel).

Наилучший известный мне способ частичного решения этой проблемы — прогонять бенчмарки и мониторить выполняемую реальную работу:

  1. Замерять бенчмарком, какой объём работы может выполнять сервер, прежде чем начнут возникать ошибки или нежелательные уровни задержек.

  2. Отслеживать, какой объём работы выполняет сервер на текущий момент.

  3. Сравнивать эти две метрики, а не показатель использования CPU.


  1. Bogo ops — это, вероятно, отсылка к BogoMIPS, то есть к «фиктивному» («bogus») бенчмарку, выполняемому Linux при запуске для крайне приблизительной оценки производительности CPU.

  2. Одно из основных ограничений работы процессоров — это необходимость достаточно быстрого рассеяния тепла. Когда работает только одно ядро, процессор может дать этому ядру немного свободы в тепловыделении, которое не тратится на другие ядра, чтобы оно работало быстрее, но это невозможно, если работают все ядра. Энергопотребление работает похожим образом и в некоторых средах тоже может быть ограничением (в десктопах обычно такого не бывает, но часто встречается на серверах).