На 16-ядерном сервере средняя загрузка CPU равна 25%, но p95 задержки растёт с нагрузкой. Какой механизм может объяснить это наблюдение?
Средняя загрузка CPU может скрывать насыщение одного ядра или отдельного последовательного вычислительного контура. При 16 ядрах загрузка одного ядра на 100% даст около 6,25% среднего CPU, поэтому показатель 25% не доказывает отсутствие CPU-узкого места.
Агрегированная загрузка CPU появилась как удобный показатель общей занятости вычислительного ресурса. На однопроцессорных системах она хорошо приближалась к состоянию главного вычислительного ресурса, но в многопроцессорных системах стала лишь средним значением по ядрам.
Рост числа ядер повысил производительность параллельных задач, однако последовательные участки, ограничения планировщика, привязка потоков и однопоточные обработчики по-прежнему могут ограничивать пропускную способность. Поэтому анализ производительности стал требовать детализации агрегированных метрик.
Если ориентироваться только на средний CPU, команда может ошибочно искать узкое место в базе данных, сети или диске. При этом очередь запросов к занятому ядру растёт, задержка увеличивается, а остальные ядра остаются недогруженными.
Неверный вывод особенно опасен при сравнении конфигураций: увеличение общего числа ядер может почти не изменить результат, если критический участок не распараллеливается. Среднее значение также может скрывать кратковременное насыщение, если интервал агрегации слишком велик.
Нужно анализировать загрузку каждого ядра, распределение потоков по ядрам, длину очереди runnable-задач и время процессора, потреблённое критическими потоками. Полезно дополнить эти данные профилированием: оно показывает, какой участок обработки занимает вычислительное время и действительно ли запросы ждут выполнения на CPU.
Механизм обычно выглядит так: поток или однопоточный обработчик последовательно принимает работу, его вычислительный контур насыщается, новые задачи ждут своей очереди, а остальные ядра не могут эффективно использоваться. В результате растут прежде всего компоненты задержки, связанные с ожиданием планирования или выполнения.
Подтверждением гипотезы будет связь роста задержки с загрузкой конкретного ядра или группы потоков, а также уменьшение задержки после устранения ограничения: распараллеливания участка, изменения распределения потоков или горизонтального разделения работы. Одного факта высокой загрузки ядра недостаточно: оно может выполнять фоновую работу, не связанную с тестируемым запросом.
Важно различать утилизацию CPU и пропускную способность. Даже полностью занятое ядро может не быть узким местом, если его работа не находится на критическом пути запроса. И наоборот, ограничение может проявляться при неполной загрузке CPU из-за ожидания блокировок, памяти, планировщика или другого последовательного ресурса.
Нагрузочный тест API на 16-ядерном сервере показывает 25% среднего CPU и растущий p95. Вариант с увеличением числа ядер почти не меняет результат: он прост в эксплуатации, но бесполезен, если насыщен один вычислительный контур. Перенос нагрузки на более быстрый процессор может дать эффект, но маскирует архитектурное ограничение и увеличивает стоимость.
Вариант с запуском дополнительных экземпляров сервиса может распределить последовательную работу между процессами и временно повысить пропускную способность. Его минусы — дополнительные расходы, необходимость проверить балансировку и возможное усложнение состояния приложения.
Корректное решение — сначала подтвердить гипотезу по метрикам отдельных ядер и профилированию, затем распараллелить или распределить критический участок, если это безопасно. Такой подход устраняет источник очереди, а не только увеличивает запас общего CPU.
Нет. Высокая загрузка ядра показывает конкуренцию за вычислительный ресурс, но не доказывает, что именно она определяет задержку тестируемых операций. Нужно сопоставить её по времени с ростом p95, пропускной способностью, очередями и трассировкой критического пути.
Например, полностью загруженное ядро может обрабатывать сборку статистики или другой фоновый процесс. Доказательством будет экспериментальное изменение этого ресурса или профилирование, показывающее, что запросы действительно ждут данный вычислительный контур.
Дополнительные ядра помогают только работе, которая может быть распределена между ними. Если критический участок выполняется последовательно, привязан к одному потоку или использует единственную очередь обработки, его предельная скорость остаётся прежней.
Кроме того, параллелизация может упереться в синхронизацию, память или другой общий ресурс. Поэтому после добавления ядер нужно повторно проверить распределение работы и новое узкое место, а не считать проблему решённой по одному показателю общего CPU.
Нужно наблюдать проблему во времени и сопоставлять несколько признаков: загрузку конкретного ядра, миграцию потоков, длину очереди runnable-задач, задержку операций и результаты повторных прогонов. Если одно и то же ядро стабильно насыщено, а задержка растёт вместе с очередью, гипотеза о локальном ограничении сильнее.
Если же перегрузка постоянно перемещается между ядрами и исчезает при изменении распределения потоков, вероятнее дисбаланс планирования или привязки. В обоих случаях полезен контролируемый эксперимент с изменением числа экземпляров, распределения потоков или привязки, но результат нужно оценивать по задержке и успешной пропускной способности, а не только по среднему CPU.