Worker использует один CPU: проверяем affinity до оптимизации кода

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

Увидеть разрешённый набор

taskset -pc 1234
taskset -apc 1234
cat /proc/1234/status
systemctl show worker.service -p CPUAffinity -p AllowedCPUs -p EffectiveCPUs

Здесь 1234 — PID выбранного процесса. Первая команда показывает affinity одного task, вторая с -a рассматривает все его потоки. В status найдите Cpus_allowed_list. Имена CPU — логические номера, поэтому диапазон 0-3 означает четыре логических CPU, а не обязательно четыре физических ядра.

Маска наследуется при создании дочернего процесса, поэтому полезно проверить и родителя. Если приложение самостоятельно закрепляет потоки, его настройка может переопределить исходное ожидание. Проверка только главного PID тогда пропустит важное различие. Параметры запроса affinity: taskset.

Отделить разрешение от потребности

Широкая маска не заставляет программу использовать несколько CPU. У неё может быть единственный вычисляющий поток, блокировка, последовательный алгоритм или слишком маленькая порция работы. Посмотрите потоки через ps -L -p 1234 -o pid,tid,psr,pcpu,comm. Поле psr показывает CPU последнего выполнения, а не число разрешённых CPU. Не делайте вывод о закреплении по одному снимку psr.

Если маска узкая, выясните происхождение. Для systemd посмотрите unit и drop-in через systemctl cat worker.service. CPUAffinity задаёт начальный набор для процессов, AllowedCPUs ограничивает группу через контроллер cpuset при поддерживаемой конфигурации. Итоговый доступ определяется пересечением ограничений, включая родительскую группу и реально доступные CPU.

Проверить изменение на стенде

taskset -c 2,3 /usr/local/bin/worker --input sample.dat

Это пример явного запуска на двух CPU, если они доступны в текущем окружении. Сначала выберите номера по данным вашей машины. Применение taskset к уже работающему PID — временное вмешательство; следующий запуск службы вернёт конфигурацию из unit. Для устойчивого исправления меняйте источник настройки и повторяйте запуск в согласованном окне.

Сравните длительность одного входа и число реально занятых потоков. Расширение affinity иногда ухудшает результат из-за конкуренции за кэш и переноса памяти между узлами NUMA. Поэтому цель проверки — понять ограничение и измерить эффект, а не всегда разрешить все CPU.

Разобрать снимок с разными потоками

Допустим, taskset для главного PID показывает список 0-7, а проверка с -a обнаруживает у вычисляющих TID только 2. В ps -L эти TID потребляют CPU по очереди, а остальные спят. Источник ограничения может находиться в коде закрепления потоков, а не в unit. Если все TID имеют 0-7, но активен только один, сначала исследуйте последовательный участок программы.

Другой случай: CPUAffinity=0-7, AllowedCPUs=2-3 и EffectiveCPUs=2-3. Разрешение на уровне процесса шире фактической границы cgroup. Расширять taskset до 0-15 бесполезно, пока группа ограничена двумя CPU. Сохраните оба набора в отчёте и определите, намеренно ли выбран cpuset для изоляции соседних служб.

Закрепить причину

В рабочей записи оставьте прежнюю маску, источник настройки и результат повторного теста. Если маска была широкой с самого начала, переключайтесь на профилирование приложения. Это экономит время команды: гипотеза о системном закреплении проверена наблюдаемыми данными.

Посмотрите программу курса Linux и Bash для DevOps-инженеров. Управление процессами и сервисами поможет связывать параметры запуска с поведением приложения: от поиска родителя до проверки настроек systemd и журналов.

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.