Осведомленность о NUMA и привязка ЦП для выделенных серверов

16 мин чтения - 16 июня 2026 г.

hero section cover
Содержание
  • Понимание NUMA и привязка ЦП для выделенных серверов
  • Как работает NUMA на серверах с несколькими сокетами
  • Проверка топологии NUMA в Linux
  • Привязка к ядрам процессора и политики работы с памятью
  • Выбор стратегии в зависимости от рабочей нагрузки
  • Настройки BIOS и ядра, которые необходимо проверить
Поделиться

Как проверить топологию NUMA и привязать рабочие нагрузки Linux к нужным ядрам и памяти. Охватывает numactl, taskset, systemd, настройки BIOS и стратегии для конкретных рабочих нагрузок.

Понимание NUMA и привязка ЦП для выделенных серверов

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

Как работает NUMA на серверах с несколькими сокетами

Узел NUMA (Non-Uniform Memory Access) — это группа ядер ЦП, связанных с локальным блоком ОЗУ через выделенный контроллер памяти. На двухпроцессорном сервере обычно имеется два узла. Любое ядро может читать любой адрес, но локальный доступ занимает примерно 80 нс, в то время как переход между сокетами через UPI от Intel или Infinity Fabric от AMD занимает около 130–150 нс. В более крупных системах с большим количеством сокетов в худшем случае время доступа к узлу может превышать 250 нс.

Пропускная способность следует той же схеме. Двухсокетная система на базе Sapphire Rapids может поддерживать около 600 ГБ/с при обращении ядер к локальной памяти, но пропускная способность межсокетного соединения составляет лишь небольшую часть этого значения, поэтому трафик, проходящий через него, быстро создает узкое место. Процессоры с большим количеством ядер делают эту структуру более детализированной: технология Sub-NUMA Clustering (SNC) от Intel и Nodes Per Socket (NPS) от AMD разделяют каждый сокет на несколько доменов NUMA, так что «двухсокетная» система может легко представлять Linux четыре или восемь узлов.

Без поддержки NUMA планировщик Linux будет без проблем перемещать поток между сокетами, в то время как его рабочий набор останется на исходном узле. Каждый последующий доступ становится удаленным. Видимым симптомом является высокая загрузка ЦП при низкой фактической пропускной способности, поскольку ядра тратят время на ожидание памяти. Устройства ввода-вывода усугубляют эту проблему. Графический процессор (GPU) или сетевая карта (NIC) подключены к определенному корню PCIe, который принадлежит одному узлу NUMA. Если процесс, подающий данные на него, работает на другом сокете, каждая передача DMA проходит через межсоединительную магистраль.

Проверка топологии NUMA в Linux

Четыре инструмента охватывают практически все необходимое:

  • lscpu для быстрого обзора сокетов и узлов.
  • numactl --hardware для получения данных об общем объеме памяти узлов и матрицы расстояний между узлами.
  • numastat для счетчиков попаданий/промахов на процесс.
  • lstopo (из hwloc) для иерархии кэша и локальности устройств PCIe.

Начните с numactl --hardware. Здесь перечислены все узлы, ядра и память, относящиеся к ним, а также матрица расстояний. Значение 10 означает локальность, 20+ — удаленность. Если вы видите один узел на многопроцессорной системе, значит в вашем BIOS включена функция чередования узлов (Node Interleaving), которая скрывает топологию; сначала исправьте это (см. ниже).

Для конкретного процесса numastat -p <PID> показывает, где фактически выделена его память. Важны четыре счетчика:

  • numa_hit: память, выделенная на предполагаемом узле. Желательно, чтобы это значение было высоким.
  • numa_miss: целевой узел был заполнен, выделение перешло в другое место.
  • numa_foreign: другой узел пытался выделить память локально, но не смог, что указывает на нехватку памяти.
  • other_node: страницы, выделенные на узле, отличном от того, на котором работает процесс. Высокие значения здесь являются классическим признаком плохой привязки.

Для рабочих нагрузок на GPU или сетевые карты запустите lstopo-no-graphics и посмотрите, к какому узлу NUMA подключено каждое устройство PCIe. Если ядра, управляющие устройством, находятся на другом узле, это первое, что нужно исправить.

Привязка к ядрам процессора и политики работы с памятью

Привязка к процессору (или аффинность к процессору) связывает процесс с конкретными ядрами, так что планировщик не может его перенести. Сама по себе этого недостаточно, поскольку Linux по умолчанию использует политику памяти «первого касания»: страницы выделяются на том узле, который первым записывает в них данные. Если поток запускается на неправильном узле до того, как он будет привязан, его память останется там. Необходимо контролировать как размещение, так и выделение памяти одновременно.

Три инструмента покрывают типичные случаи:

ИнструментУправлениеИспользуется для
tasksetТолько ядра ЦПБыстрая однократная привязка существующего процесса
numactlЯдра ЦП и памятьЗапуск рабочих нагрузок со строгой локальностью
systemdЯдра ЦП и память, постояннаяСлужбы, требующие фиксации при перезагрузке

numactl поддерживает четыре политики использования памяти:

  • --membind=N: выделять только на узле N, завершать с ошибкой, если он заполнен.
  • --preferred=N: отдавать предпочтение узлу N, при необходимости переключаться на другие.
  • --interleave=all: циклическое распределение между узлами для равномерного распределения пропускной способности.
  • --localalloc: выделять на том узле, на котором находится работающий процессор.

Привязка рабочей нагрузки к одному узлу

Сначала определите, какие ядра принадлежат вашему целевому узлу:

numactl --hardware

Затем запустите приложение, привязанное к этому узлу как по ядрам, так и по памяти:

numactl --cpunodebind=0 --membind=0 ./your_application

Для уже запущенного процесса настройте привязку к ЦП с помощью taskset:

taskset -cp 0-7 <PID>

Чтобы настройка сохранилась после перезагрузки, задайте ее в модуле systemd:

[Service]
CPUAffinity=0-7
NUMAPolicy=bind
NUMAMask=0

Перезагрузите и перезапустите:

sudo systemctl daemon-reload && sudo systemctl restart <service>

При ручной привязке отключите автобалансировку ядра, чтобы она не мешала вашему размещению:

sysctl -w kernel.numa_balancing=0

Добавьте это в /etc/sysctl.conf для сохранения настроек. Затем проверьте с помощью numastat -p <PID> в течение нескольких минут реальной нагрузки. Если other_node остается близким к нулю, фиксация работает.

Выбор стратегии в зависимости от рабочей нагрузки

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

Рабочая нагрузкаПолитикаПочему
Базы данных (PostgreSQL, MySQL, SQL Server)--cpunodebind + --membindБольшие общие буферы, пути запросов, чувствительные к задержкам
Кэш в памяти (Redis, Memcached)Привязка к одному узлуВсе происходит через доступ к ОЗУ, задержка при удаленном доступе проявляется сразу
Обучение и инференция в области ИИ/машинного обученияПривязка к узлу NUMA графического процессораИзбегает передачи тензоров через корни PCIe
Аналитика (Spark, Elasticsearch)--interleave=allБольшой рабочий набор требует пропускной способности на всех узлах
API, чувствительные к задержкам, торговляСтрогая привязка к выводам + IRQПредсказуемость важнее пиковой пропускной способности
Интенсивное использование сети (RoCEv2, InfiniBand)Привязка к NUMA-узлу сетевой карты, выделение ядер для IRQОбеспечивает локальную обработку прерываний, не мешая потокам приложений

Специально для рабочих нагрузок на GPU запустите lstopo , чтобы определить, на каком узле NUMA находится GPU, а затем запустить процесс обучения или инференса с numactl --cpunodebind=N --membind=N для того же N. Это один из самых простых способов добиться успеха на многопроцессорном сервере с GPU, поскольку размещение по умолчанию планировщиком часто бывает неверным.

Для рабочих нагрузок HPC и MPI, охватывающих оба сокета, привяжите каждый ранг к отдельному узлу с помощью localalloc , а не чередуя все. Каждый ранг получает локальную память, и параллелизм происходит на уровне ранга.

Одно практическое замечание: если вы привязываете к одному узлу, оставьте на нем 2–4 ГБ свободного места. Узел, работающий почти на полной загрузке, запускает процесс освобождения памяти, что обходится вам в ту задержку, которую вы пытались сэкономить.

Настройки BIOS и ядра, которые необходимо проверить

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

  • Чередование узлов: отключите его. Когда эта функция включена, BIOS представляет всю память как единый плоский пул и полностью скрывает NUMA от ОС. numactl --hardware В этом случае на многопроцессорном компьютере будет отображаться один узел.
  • Sub-NUMA Clustering (Intel) или Nodes Per Socket (AMD): включите на процессорах с большим количеством ядер, если требуется более точная локальность. Подтверждается lscpu после перезагрузки.
  • vm.zone_reclaim_mode: установите значение 0 для большинства производственных серверов. Ненулевое значение активно освобождает локальную память вместо выделения удаленно, что может привести к вытеснению полезного кэша страниц.
  • kernel.numa_balancing: оставьте включенным для рабочих нагрузок общего назначения, отключите при ручной привязке. Автобалансировщик будет перемещать страницы и потоки способами, противоречащими вашей политике.

Если вы выполняете настройку NUMA на «голом железе», где вы контролируете BIOS, параметры ядра и аффинность IRQ, вы можете применить все вышеперечисленное, не обходя абстракции гипервизора. Это главная причина, по которой такая работа проще на выделенном оборудовании, чем в облачных виртуальных машинах.

Для многопроцессорных выделенных серверов с полным root-доступом см. выделенные серверы FDC.

Блог

События этой недели

Другие статьи
Настроенные профили для оптимизации рабочей нагрузки Linux-сервера

Настроенные профили для оптимизации рабочей нагрузки Linux-сервера

Как выбирать, применять и настраивать профили для GPU, баз данных и Linux-серверов с высокой пропускной способностью, с примерами и советами по развертыванию Ansible.

16 мин чтения - 9 июня 2026 г.

Тюнинг Linux OOM Killer для VPS: практическое руководство

12 мин чтения - 8 июня 2026 г.

Другие статьи
background image

У вас есть вопросы или вам нужно индивидуальное решение?

icon

Гибкие варианты

icon

Глобальный охват

icon

Мгновенное развертывание

icon

Гибкие варианты

icon

Глобальный охват

icon

Мгновенное развертывание