NUMA (Non-Uniform Memory Access — Неоднородный доступ к памяти)

NUMA — архитектура памяти многопроцессорных систем, где скорость доступа зависит от расположения памяти относительно процессора.

2026.03.30                  


NUMA (Non-Uniform Memory Access — Неоднородный доступ к памяти)NUMA (Non-Uniform Memory Access — Неоднородный доступ к памяти) Тема NUMA (Non-Uniform Memory Access — Неоднородный доступ к памяти) критически важна для понимания производительности современных серверов, баз данных и высоконагруженных систем. Непонимание принципов NUMA часто приводит к тому, что мощное «железо» работает медленнее, чем ожидается.

1. Введение: Что такое NUMA?

NUMA (Non-Uniform Memory Access) — это архитектура компьютерной памяти, используемая в многопроцессорных системах, где время доступа к памяти зависит от того, где расположена память относительно процессора, который к ней обращается.

Простыми словами: Память, которая «ближе» к процессору, работает быстрее, чем память, которая «далеко».

Сравнение с UMA

Чтобы понять NUMA, нужно вспомнить его противоположность — UMA (Uniform Memory Access):

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

2. Как это работает «под капотом»

В современных серверах (Intel Xeon, AMD EPYC) контроллеры памяти встроены непосредственно в процессор.

Структура узла (Node)

Система делится на NUMA-узлы. Обычно один узел = один физический процессор (сокет) + память, подключенная к нему.

Локальная память (Local Memory):

  • Память, подключенная напрямую к данному процессору. Доступ быстрый (низкая латентность, высокая пропускная способность).

Удаленная память (Remote Memory):

  • Память, подключенная к другому процессору. Чтобы получить к ней доступ, запрос идет через межпроцессорную шину (Intel QPI/UPI или AMD Infinity Fabric). Это добавляет задержку.

Пример задержек (латентности)

  • Доступ к локальной памяти: ~100 нс.
  • Доступ к удаленной памяти: ~150–200 нс (и выше, в зависимости от топологии).
  • Разница может достигать 30–50%., что критично для баз данных и научных вычислений.

3. Влияние NUMA на производительность

Если операционная система или приложение не учитывают NUMA, могут возникнуть следующие проблемы:

  1. Cross-Node Memory Access: Поток выполняется на Ядре 1 (Узел 0), а выделяет память на Узле 1. Каждое обращение к этой памяти идет через межпроцессорную шину.
  2. Перегрузка шины (Interconnect Saturation): Если много потоков постоянно читают чужую память, шина между процессорами забивается, замедляя работу всей системы.
  3. False Sharing (Ложное разделение): Два ядра из разных узлов модифицируют переменные, находящиеся в одной кэш-линии. Это вызывает постоянную синхронизацию кэшей между узлами.

Когда NUMA важен?

  • Базы данных (PostgreSQL, Oracle, MS SQL).
  • Высоконагруженные веб-серверы.
  • Научные вычисления (HPC).
  • Обычные офисные ПК (OS сама хорошо справляется).
  • Системы с одним процессором (там NUMA нет или он не влияет).

4. Диагностика и мониторинг (Linux)

Большинство серверов работают на Linux. Вот набор инструментов для анализа.

1. Просмотр топологии NUMA

  • Команда numactl показывает, как видят систему ядро и железо.
numactl --hardware
# или сокращенно
numactl -H

Вывод:

  • available: 2 nodes (0-1) — сколько узлов.
  • node 0 cpus: — какие ядра принадлежат узлу 0.
  • node 0 size: — сколько памяти локально.
  • node 0 free: — сколько свободно.
  • node distances: — матрица задержек. (10 — локально, 20+ — удаленно).

2. Просмотр распределения памяти

Команда vmstat может показать активность NUMA (в новых версиях):

vmstat -w -n 1

Смотрите на колонки free, si, so (свопинг), но для NUMA важнее специализированные утилиты.

Утилита numastat показывает статистику распределения памяти по узлам:

numastat
  • numa_hit: Успешные выделения локальной памяти (хорошо).
  • numa_miss: Выделения удаленной памяти (плохо, если много).
  • numa_foreign: Выделения на этом узле для процесса, бегущего на другом (плохо).

3. Анализ производительности

Инструмент perf может показать события, связанные с памятью:

perf stat -e memory_bandwidth:local_bandwidth.used,mem_inst_retired.all_loads -a sleep 5

(Поддержка событий зависит от процессора).


5. Стратегии оптимизации

Оптимизация происходит на трех уровнях: BIOS, ОС и Приложение.

Уровень 1: BIOS / UEFI

В настройках сервера часто есть опция Node Interleaving.

  • Disable (Рекомендуется для производительности): Включает режим NUMA. ОС видит узлы и может оптимизировать.
  • Enable: Принудительно делает память однородной (UMA). ОС видит одну большую кучу памяти.
    • Зачем включать? Если приложение вообще не умеет работать с NUMA и начинает глючить, или для совместимости со старым ПО. Но производительность может упасть на 10–20%.

Уровень 2: Настройка ОС (Linux)

Политики выделения памяти

Вы можете управлять поведением через numactl.

Interleave (Чередование):

  • Память распределяется циклически по всем узлам. Хорошо, если приложение использует много потоков на всех ядрах равномерно.
    numactl --interleave=all ./my_application

Bind (Привязка):

  • Жесткая привязка процесса к узлу. Процесс использует только локальную память и ядра этого узла. Лучшая производительность для изолированных задач.
    numactl --cpunodebind=0 --membind=0 ./my_application

Prefer (Предпочтение):

  • Попытаться выделить локально, если нет места — взять удаленную.
    numactl --preferred=0 ./my_application

Отключение прозрачной огромных страниц (THP)

Иногда THP (Transparent Huge Pages) конфликтует с NUMA, вызывая задержки. Для баз данных (например, MongoDB, Redis, Oracle) часто рекомендуют отключать THP.

echo never > /sys/kernel/mm/transparent_hugepage/enabled

Уровень 3: Настройка приложения

  • Affinity (Аффинность): Привязка потоков приложения к конкретным ядрам CPU.
  • Локальность данных: Архитектура приложения должна стремиться к тому, чтобы данные обрабатывались тем же ядром, которое их создало или загрузило.
  • Разделение баз данных: Если у вас 2 сокета, можно запустить два экземпляра БД, каждый на своем узле NUMA, разделив нагрузку.

6. NUMA в виртуализации (vNUMA)

Если вы используете виртуальные машины (VMware, KVM, Hyper-V), важно настроить vNUMA.

  • Проблема: Гипервизор может распределить виртуальные ядра VM по разным физическим процессорам хоста, а память выделить из одного узла. Это убьет производительность внутри VM.
  • Решение:
    • Включить поддержку vNUMA в настройках VM.
    • Размер VM не должен превышать размер физического NUMA-узла хоста (если возможно).
    • Использовать резервирование памяти (Memory Reservation), чтобы гипержор не свопил память VM.

Пример для KVM/QEMU (в XML конфигурации):

<cpu mode='host-passthrough' check='none'>
  <topology sockets='1' cores='8' threads='1'/>
  <numa>
    <cell id='0' cpus='0-7' memory='16777216' memAccess='readonly'/>
  </numa>
</cpu>

7. Чек-лист: Что делать прямо сейчас?

Если вы администрируете сервер, вот пошаговый план действий:

Проверьте топологию:

  • Выполните numactl -H. Убедитесь, что система видит несколько узлов (если у вас многопроцессорный сервер).

Проверьте балансировку:

  • Выполните numastat -m. Если видите много numa_miss или numa_foreign для важных процессов, значит, есть перекос.

Проверьте BIOS:

  • Убедитесь, что Node Interleaving выключен (Disabled), чтобы ОС могла управлять NUMA осознанно.

Настройте критичные сервисы:

  • Для СУБД (PostgreSQL, MySQL) используйте привязку к ядрам и памяти через numactl.

Пример для запуска PostgreSQL:

    numactl --cpunodebind=0 --membind=0 pg_ctl start

(Если у вас 2 узла, можно запустить инстансы на обоих или разделить процессы).

Мониторинг:

  • Добавьте сбор метрик numastat в вашу систему мониторинга (Zabbix, Prometheus), чтобы видеть динамику удаленного доступа к памяти.

8. Распространенные мифы

  • Миф: «NUMA всегда нужно отключать».
    • Реальность: Отключать (включать Interleaving) нужно только если приложение ведет себя нестабильно. Для производительности NUMA должен быть включен.
  • Миф: «ОС сама все оптимизирует».
    • Реальность: Современные ядра (Linux 4.x+, Windows Server 2016+) умные, но они не знают бизнес-логику вашего приложения. Для высоконагруженных систем ручная настройка дает прирост 10–30%.
  • Миф: «NUMA нужен только для 4+ сокетных систем».
    • Реальность: Даже в двухпроцессорных системах (2 Socket) разница между локальной и удаленной памятью существенна.

Заключение

Архитектура NUMA — это плата за масштабируемость. Она дает возможность устанавливать в сервер десятки ядер и терабайты памяти, но требует внимательного отношения к расположению данных и потоков.

Главное правило:

  • Держите вычисления (CPU) и данные (Memory) как можно ближе друг к другу в пределах одного NUMA-узла.