АрхитектураНадёжность и производительностьИнженер по производительности

По каким признакам в профиле отличить нехватку CPU от ожидания блокировок?

По каким признакам в профиле отличить нехватку CPU от ожидания блокировок?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

При нехватке CPU потоки преимущественно выполняются на процессорах: растут загрузка CPU, время on-CPU и очередь runnable-потоков. При ожидании блокировок потоки проводят значительную часть времени off-CPU в состоянии ожидания, а профиль показывает точки блокировки и конкуренцию за общий ресурс.

Одной загрузки CPU недостаточно: при блокировках она может быть низкой, высокой из-за активного ожидания или неравномерной по ядрам. Нужно сопоставить CPU-профиль, wall-clock-профиль, состояния потоков и данные о конкуренции.

Исторический контекст

Профилирование развивалось из-за ограничения агрегированных метрик: средняя загрузка процессора и среднее время ответа показывают симптом, но не объясняют, где тратится время. Для этого разделяют on-CPU-профилирование, фиксирующее выполняющийся код, и off-CPU-анализ, показывающий ожидание блокировок, ввода-вывода, планировщика или других событий.

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

Постановка проблемы

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

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

Подробное решение

При нехватке CPU обычно наблюдаются следующие признаки:

  • высокая загрузка процессоров и рост времени on-CPU;
  • много runnable-потоков, ожидающих планирования;
  • профиль содержит дорогие вычислительные функции, а не одну доминирующую точку блокировки;
  • уменьшение объёма вычислений или распределение работы по дополнительным ядрам улучшает результат.

При ожидании блокировок характерны другие признаки:

  • потоки долго находятся не на CPU, а в состоянии ожидания;
  • wall-clock-профиль показывает стек блокировки или ожидания, которого почти нет в CPU-профиле;
  • много потоков конкурируют за один mutex, монитор, очередь, пул соединений или другую синхронизируемую структуру;
  • время удержания ресурса или частота его захвата коррелируют с ростом задержки.

Нужно различать ожидание блокировки и активное ожидание. При пассивном ожидании CPU может простаивать, а при spinlock или неудачно реализованном busy-wait процессор расходуется даже без полезной работы. Поэтому высокая загрузка CPU не доказывает, что приложение выполняет полезные вычисления.

Практический метод — сравнить несколько представлений одной нагрузки: CPU-время, wall-clock-время, состояния потоков, распределение задержек захвата блокировок и загрузку ресурсов. Если блокировка часто удерживается из-за медленной операции, оптимизировать нужно не только сам механизм синхронизации, но и работу внутри критической секции.

Ограничение профилирования заключается в том, что выборка может пропустить редкие или короткие блокировки, а инструментирование — изменить тайминги. Результаты следует сопоставлять с нагрузочными тестами и метриками приложения, не делая вывод по одному графику.

Ситуация из практики

Сервис начал медленно обрабатывать запросы после увеличения числа рабочих потоков. CPU был загружен примерно наполовину, поэтому рассматривались три варианта: добавить процессоры, увеличить число экземпляров или исследовать конкуренцию.

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

Wall-clock-профиль показал, что большинство потоков ожидает одну блокировку вокруг общей структуры кэширования, а CPU-профиль не содержал сопоставимой по времени вычислительной функции. Команда сократила критическую секцию и разделила структуру на независимые сегменты; после этого ожидание уменьшилось, а масштабирование потоками стало приносить пользу.

Что кандидаты часто упускают

  1. Почему низкая загрузка CPU не доказывает отсутствие проблем с производительностью?

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

  1. Может ли ожидание блокировки сопровождаться высокой загрузкой CPU?

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

  1. Почему устранение одной горячей блокировки иногда не даёт ускорения?

После устранения первого ограничения проявляется следующий: например, другая блокировка, очередь к пулу соединений или ограничение внешней зависимости. Кроме того, блокировка могла быть лишь местом, где накапливалось ожидание, а исходная причина находилась в слишком долгой операции внутри критической секции. Поэтому после изменения нужно повторить измерение и проверить, переместилось ли узкое место, а не считать успехом только снижение одной метрики.