Сервер NUMA Локальность и сродство к памяти процессора определяют, насколько близко потоки работают к своей оперативной памяти и насколько постоянными остаются задержки в стеках хостинга. Я покажу вам на практике, как можно добиться ощутимо большей пропускной способности с помощью распознавания топологии, стратегий сродства и путей ввода-вывода, близких к узлу, и Латентность заметно ниже.
Центральные пункты
Чтобы быстро сориентироваться, я кратко излагаю основные идеи, а затем подробно объясняю шаги и подкрепляю их примерами, чтобы вы сразу поняли, с чего следует начать, чтобы Местность и Affinity с пользой для дела. Я подчеркиваю четкие взаимосвязи между потоками, памятью и вводом-выводом, чтобы вы могли четко определить приоритеты и Решения встречайте. Я также определяю сценарии, в которых Interleave имеет смысл, не размывая критические пути, и показываю, как вы можете продемонстрировать реальный прогресс с помощью мониторинга и Ошибка можно избежать. Для виртуализированных сред я даю советы по размещению vCPU и vRAM, чтобы гостевые системы не проскальзывали между несколькими узлами и Удаленный-доступы взрываются. Наконец, я переведу полученные результаты в краткую дорожную карту, чтобы вы могли действовать структурированно и делать каждый шаг в правильном направлении. измеримый безопасно.
- Местность Во-первых: держите потоки вблизи собственной оперативной памяти, избегайте удаленных.
- Affinity исправить: Связывать ядра и память вместе с помощью политики.
- Топология читать: Узлы, ядра, устройства PCIe на сокет.
- Пути ввода/вывода пучок: Соедините сетевую карту, NVMe и приложение в одном узле.
- ярмарки вместо того, чтобы гадать: P95/ P99, удаленный доступ и отслеживание пропускной способности.
Понимание топологии NUMA
Прежде чем перемещать рабочие нагрузки, я читаю Топология сервера: сколько существует узлов NUMA, сколько ядер и какой объем оперативной памяти подключен к каждому узлу. Я также обращаю внимание на то, какие устройства PCIe - например, сетевые карты или NVMe SSD - подключены к какому сокету, поскольку от этого зависят пути прерываний и доступа к памяти, а также объем оперативной памяти, подключенной к каждому узлу. Латентность характеризуется. Узел обеспечивает локальный доступ к памяти на небольшом расстоянии; все, что выходит за его пределы, требует затрат времени и пропускной способности. Чем больше масштабируется машина с множеством сокетов, тем больше удаленный доступ влияет на время отклика и съедает пропускную способность. Пропускная способность. Для понятного введения в аппаратную логику я считаю компактным Узлы NUMA с первого взгляда, сознательно учитывать границы узлов и избегать неправильных распределений.
На практике я начинаю с краткого описания топологии и документирую ее, чтобы впоследствии иметь возможность выводить решения о сродстве понятным способом. Полезные команды:
Ядра # и назначение NUMA
lscpu -e=CPU,Core,Socket,Node
Обзор аппаратного обеспечения # NUMA
numactl --hardware
# Назначьте устройства PCIe своим узлам NUMA
lspci -nn | grep -E "Ethernet|Non-Volatile"
for d in /sys/bus/pci/devices/*; do echo -n "$d: "; cat $d/numa_node; done
Главное, чтобы вы Корневой комплекс PCIe и слоты устройств к сокетам. Два порта одной и той же сетевой карты могут быть назначены разным узлам; это влияет на то, где лучше всего распределяются очереди RX/TX и IRQ. То же самое относится и к NVMe: современные контроллеры имеют несколько очередей, которые следует привязать к ядрам, расположенным близко к узлу, чтобы DMA не вызывал скачков между узлами.
Правильное использование сродства памяти процессора
С помощью CPU-Memory Affinity я жестко привязываю процессы к основным областям и обеспечиваю локальное распределение памяти, насколько это возможно, так что Темы не выходят постоянно за пределы узла. В Linux я определяю процессоры через systemd или cgroups и комбинирую это с политиками памяти, так что оперативная память предпочтительно создается на том же узле и Удаленный остается минимальным. Критически важные службы - фронтенды API, кэши in-memory, базы данных - сразу же выигрывают, поскольку время ожидания контроллера памяти сокращается, а обращения к кэшу становятся более частыми. Однако слишком жесткие ограничения могут ограничить планирование, поэтому я подкрепляю каждую настройку бенчмарками и наблюдаю за значениями P95/P99 для выявления заметных эффектов на Пользователь-опыт. Компактное введение в Affinity in hosting поможет вам начать работу: Сродство и осознание NUMA предоставить необходимые инструменты для чистого размещения.
Решающим фактором является Принцип первого касанияПамять создается на том узле, который первым записывает страницу. Поэтому инициализируйте большие кучи или буферы на целевых ядрах узла, на котором впоследствии будет работать служба - в идеале с уже установленной политикой CPU и памяти (например, через systemd unit или numactl). Если вы запустите холод на узле 0, а затем переместите потоки на узел 1, большинство страниц останутся удаленными. Для куч с большим временем выполнения стоит использовать „pre-touch“ во время загрузки, чтобы страницы ротировались локально, а затем оставались теплыми.
Осознание NUMA в стеке хостинга
Операционная система с поддержкой NUMA, подходящий гипервизор и приложения с функцией объединения потоков раскрывают весь свой потенциал вместе. Потенциал. ОС предпочитает локальное размещение, когда на узле есть свободные ресурсы, а гипервизор распределяет ВМ таким образом, чтобы vCPU и vRAM не расходились и Местность поддерживается. В приложении я разделяю рабочие пулы на каждый узел и держу очереди локальными, вместо того чтобы управлять глобальными пулами на всех узлах. Я организую процессы базы данных, демоны кэша и экземпляры веб-сервера на основе каждого узла так, чтобы горячие пути оставались короткими и Джиттер уменьшается. Это повышает согласованность и предсказуемость под нагрузкой, что напрямую влияет на предсказуемость SLA в евро и позволяет избежать дорогостоящего перераспределения ресурсов.
На уровне ингресса я забочусь о том. Сродство к узлу сеансов, например, с помощью липкой маршрутизации или последовательного хэширования (например, по IP-адресу клиента или токенам сеанса), чтобы запросы возвращались к „своему“ узловому рабочему и кэшу. Для stateful-сервисов я планирую реплики на узел и локально балансирую доступ к чтению; я выравниваю пути записи с помощью асинхронной репликации или пакетной обработки, чтобы избежать межузлового пинг-понга.
Планирование услуг на каждом узле
Я группирую слои в стеке таким образом, чтобы у каждого слоя была четкая ссылка на узел и Пути Оставайтесь короткими. Классическое разделение: web/API на узле, app worker рядом с ним, плюс локальный кэш; база данных также располагается рядом с узлом, если влезает объем оперативной памяти, и IO-путь не прерывается. Я перемещаю задания по созданию отчетов, резервному копированию или пакетной обработке на менее критичные узлы, чтобы интерактивные запросы не пострадали. Я избегаю больших монолитных экземпляров, поскольку они часто пересекают границы узлов и, следовательно, создают удаленную нагрузку, которая Производительность размытый. Небольшие экземпляры с репликацией на узел часто обеспечивают лучшую пропускную способность в повседневном использовании, поскольку они соблюдают правила NUMA и сглаживают пики.
Для планирования мощностей я рассчитываю запас по мощности отдельно для каждого узла: буфер CPU для всплесков, буфер RAM против OOM и отдельные поля для страничного кэша. Таким образом, я предотвращаю непреднамеренное удаленное переключение ядра. Я определяю четкие пути переключения при обходе отказа: если узел выходит из строя, заменяющие его экземпляры могут работать между узлами, но я ограничиваю их параллелизм до восстановления исходного узла - это позволяет поддерживать стабильную общую задержку.
Настройка сродства процессора: Методы и подводные камни
Для распределения ядра я использую systemd с CPUAffinity или cgroups с cpuset.cpus, чтобы службы имели фиксированную Основные направления получать. При распиновке я обращаю внимание на пары гиперпотоков, потому что два логических потока физического блока делят ресурсы и могут замедлить друг друга, если я неудачно их объединю. Советы создать. Пути с высокой латентностью - завершение TLS, вход в API, чтение кэша - получают эксклюзивные ядра, в то время как журналы, сжатие или резервное копирование перемещаются в другие пулы. Слишком узкие пулы без буферов приводят к образованию очередей, поэтому я учитываю резерв и проверяю переключение контекста, длину runqueue и IRQ-распределение. Из этого наблюдения я делаю вывод о том, следует ли мне открыть ядра шире или сконцентрировать их дальше, пока распределение задержек не станет чистым, а пики P99 не станут тише.
Для дальнейшего уменьшения джиттера я выборочно устанавливаю такие переключатели ядра, как nohz_full и rcu_nocbs для ядер с исключительной задержкой, изолируйте их от системных служб и намеренно размещайте IRQ только на процессорах, предназначенных для этой цели. Я использую службу „irqbalance“ с осторожностью: либо настройте ее особым образом, либо отключите, если она мешает вашему ручному распределению IRQ. Я использую SCHED_FIFO/SCHED_RR редко и только с ограничениями Be, чтобы избежать инверсии приоритетов или голодания.
Политики памяти и маски NUMA
Для политики памяти я различаю предпочтительное локальное распределение, чередование между несколькими узлами и фиксированные маски NUMA с помощью cpuset.mems, так что RAM потоки, в которых фактически работают потоки. Для интерактивных сервисов я обычно устанавливаю значение „preferred“, что означает, что система распределяет локально и отклоняется только в случае нехватки, что является Удаленный-доступ ограничен. Аналитические или потоковые задания иногда выигрывают от чередования, поскольку пропускная способность распределяется между узлами и нагрузка на контроллер снижается. Фиксированные маски обеспечивают контроль, но требуют дисциплины при планировании пропускной способности, чтобы не допустить нежелательных событий OOM на узле и Услуги вмешаться. Следующая таблица классифицирует общие политики по типичным сценариям и поможет вам быстро принять решение.
| Политика | Эффект | Типичные рабочие нагрузки | Риск |
|---|---|---|---|
| Предпочтительный (местный) | Оперативная память преимущественно в локальном узле, запасной вариант на случай нехватки | Web/ API, кэши, базы данных OLTP | Небольшой дрейф при полной нагрузке на другие узлы |
| Interleave | Равномерное распределение по выбранным узлам | Потоковая передача данных, аналитика, сканирование больших объемов | Более высокая задержка при индивидуальном доступе |
| Исправлена маска NUMA | Строгая привязка к определенным узлам памяти | Строго инкапсулированные сервисы, детерминированные тесты | Риск OOM при неправильном планировании бюджета |
Следите за общесистемными переключателями: режим_восстановления_зоны Влияет на то, будет ли узел агрессивно очищать собственную память перед удаленным выделением - часто нежелательно для путей с задержкой. Прозрачные огромные страницы (THP) может вызвать миграцию страниц или спровоцировать застой; для сервисов, чувствительных к задержкам, я обычно выбираю „madvise“ и использую статические огромные страницы там, где это имеет смысл, чтобы увеличить количество обращений к TLB и уменьшить пики отказов страниц.
Привязка сетевых путей и путей ввода-вывода к узлу
Я выравниваю очереди сетевых карт (RX/TX) так, чтобы их IRQ указывали на ядра соответствующего узла, а обработка пакетов происходила там, где Приложение вычислений. То же самое относится к NVMe SSD или RAID-контроллерам: потоки ввода-вывода должны выполняться на узле, к которому устройство подключено через PCIe, чтобы пути DMA оставались короткими и устройство использовалось более эффективно. Узкие места не удается реализовать. В Linux я настраиваю маски сродства IRQ и связываю их с процессорными пулами моих служб, чтобы создать непрерывный путь. При микровсплесках в сети, таких как многочисленные рукопожатия TLS, такая близость приносит прямые плоды, поскольку пути копирования становятся короче, а кэши процессора остаются теплыми и Контекст реже. Это обеспечивает последовательный поток данных от пакета к приложению и памяти без лишних переходов между узлами.
Конкретные рычаги в сетевом стеке: RSS для аппаратного распределения по очередям, RPS/RFS для программного управления процессором и XPS для выбора TX. Я использую ethtool для назначения очередей RX группам ядра, которые работают на том же узле, что и ваши рабочие. Для хранения данных я использую blk-mq-настройка и сопоставление очередей для каждого узла; контроллеры NVMe предлагают несколько очередей отправки/завершения, которые я масштабирую и аффинитирую ≤ количеству ядер на узле. Регулярно проверяйте, срабатывают ли прерывания (cat /proc/interrupts) там, где расположены ядра вашего приложения - вы можете распознать дрейф по увеличению удаленных байтов, несмотря на стабильную нагрузку.
Структурируйте архитектуру приложений в соответствии с NUMA
На уровне приложений я создаю собственные пулы рабочих для каждого узла NUMA, поддерживаю локальные очереди и избегаю глобальных точек блокировки, так что Темы а не прыгать туда-сюда. Я настроил разделение сеансов и данных таким образом, чтобы горячие разделы оставались там, где работают запрашивающие рабочие, и Время не теряется в межузловом трафике. Для кэшей я часто использую реплики вместо центрального экземпляра, чтобы читатели получали локальные копии узлов. В клиентах Netty, Tokio, libuv или DB я привязываю циклы событий к фиксированным ядрам и обращаю внимание на близость IRQ, чтобы смена задач оставалась ограниченной и Кэши бить лучше. Такое расположение уменьшает эффект пинг-понга и делает время отклика более стабильным в течение дня.
Одним из недооцененных рычагов является Аллокатор и опции времени выполнения: Аллокаторы с поддержкой NUMA (jemalloc/tcmalloc) уменьшают межпотоковое соперничество и держат страницы ближе к ядрам, в которых находятся потоки. В стеках JVM такие опции, как NUMA awareness и pre-touch, помогают детерминировать фазы сбоев; в .NET я выравниваю потоки GC вблизи узлов и уделяю внимание серверному GC, чтобы сгладить время остановки. В Go я определяю размер GOMAXPROCS для пула узлов и держу планировщики горутин подальше от латентных ядер, которые работают близко к IRQ.
Разумное управление автобалансировкой NUMA
Автоматические механизмы балансировки NUMA в ядре могут помочь сгладить распределенную нагрузку, но я всегда проверяю, способны ли они удовлетворить мои требования. Affinity подрываются. В службах, критичных к задержкам, я отключаю или дросселирую автоматическое перемещение, если оно вытягивает потоки из локальной памяти и Советы сгенерировано. Для аналитических заданий или широкой пакетной обработки я обычно оставляю балансировку включенной, поскольку она может увеличить пропускную способность без ухудшения взаимодействия. Практическое введение в стратегии балансировки дает мне дополнительные отправные точки: Понимание балансировки NUMA показывает, когда следует использовать автоматическую систему, а когда назначать ее вручную. В итоге я принимаю решение на основе данных для каждого класса обслуживания, а не слепо принимаю глобальные настройки по умолчанию и Цели пропустить.
Когда балансировка активирована, я слежу за скоростью миграции, пиками мелких/крупных сбоев и кражей процессора на узел. Если страницы перемещаются туда-сюда циклически, я противодействую этому с помощью более жесткого пиннинга, предварительного касания и более узких масок памяти. В рабочих нагрузках с длительным последовательным сканированием, с другой стороны, балансировка может гармонизировать нагрузку, если не затронуты пути с интерактивной задержкой.
Мониторинг: измерять, сравнивать, принимать решения
Без измерений настройка остается игрой в угадайку, поэтому я отслеживаю загрузку процессора на ядро и на узел, использование памяти на узел и долю Удаленный-доступа. Для пользовательского опыта задержки P95/ P99 имеют гораздо большее значение, чем средние значения, поскольку выбросы характеризуют впечатления от SLA и Стоимость вверх. Я запускаю реалистичные профили нагрузки с холодным и теплым кэшем, потому что в обоих случаях узкие места проявляются по-разному. После каждого изменения я документирую настройки, дату тестирования и результаты, чтобы впоследствии можно было безопасно отменить изменения и Знания не теряется. Если вы также соотнесете метрики приложений - длину очереди, повторные попытки, сборку мусора - с системными показателями, вы сможете быстрее определить причину и следствие.
Практическая помощь в анализе:
- нумастат (системный и связанный с процессом) для локального по сравнению с локальным. Удаленный-Хит
- /proc/interrupts и SoftIRQ время процессора для дрейфа IRQ
- перфектные события и статистика планировщика для глубины runqueue, переключения контекста, пропусков LLC и т.д.
- fio/iperf/wrk с пулами рабочих для конкретных узлов для воспроизводимых сравнений
Оценка производится для каждого узла: Я ожидаю, что гистограммы задержек будут близки друг к другу. Если узел смещается вверх, я сначала ищу неправильное распределение нагрузки на IRQ, дрейф в страничном кэше или кучи, которые были выделены не тому узлу во время прогрева.
NUMA в виртуальных машинах и контейнерах
При виртуализации размещение vCPU и vRAM на общем узле важно для того, чтобы гостевые рабочие нагрузки не пересекались и Латентность подтягивается. Я определяю размер оперативной памяти так, чтобы она помещалась на локальном узле, и избегаю больших ВМ, которые распространяются на несколько узлов и Дрейф триггер. Для контейнеров я использую контроллеры cpuset, чтобы группы подгрупп стабильно работали на одном узле, а хранилище создавалось локально. Я предпочитаю размещать гостей с высокой нагрузкой на ввод-вывод на узле с прямым подключением к хранилищу, чтобы сохранить короткие пути DMA и IRQ-Снижение уровня шума. Это означает, что даже плотные узлы виртуализации остаются предсказуемыми и позволяют выполнять больше проектов на том же оборудовании.
Я обращаю внимание на vNUMA-Гость должен видеть ту же структуру узлов, которую физически предоставляет гипервизор. Привязка vCPU и привязка vRAM работают вместе; по возможности я перемещаю hot-adds во время окон обслуживания, потому что иначе новые страницы оказываются удаленными. В Kubernetes я устанавливаю „гарантированный“ QoS, менеджер CPU „статический“ и размещение с учетом топологии, чтобы капсулы не перемещались между узлами. Для SR-IOV/VFs я назначаю VFs на соответствующий физический узел и привязываю очереди IRQ к наборам CPU обслуживаемых ими стручков или виртуальных машин.
Целенаправленная подготовка к первому касанию, разминка и навесы
Многие ошибки производительности возникают во время НачалоКучи растут на этапе разогрева, когда приходят первые запросы - часто в центре узла. Поэтому я запускаю контролируемый разогрев для каждого узла: я запускаю экземпляры с заданной маской процессора/памяти, выполняю целевые запросы предварительной загрузки и инициализирую кэши параллельно для каждого узла. Для служб JVM я активирую предварительное обращение к куче; для баз данных я сегментирую буферные пулы по узлам. Это уменьшает последующие миграции страниц и гарантирует, что первые запросы не будут случайным образом характеризовать распределение памяти.
Настройка ядра/BIOS для постоянных задержек
Под капотом я регулирую мощность и политику прерываний:
- Установите регулятор процессора на „производительность“, ограничьте глубокие C-состояния, используйте пакетные C-состояния осторожно, чтобы Джиттер уменьшить.
- Не дросселируйте частоту памяти; сбалансированные энергетические профили часто сводят к минимуму Пропускная способность под нагрузкой.
- Избегайте модуляции спектра/часов, если постоянство важнее минимальной экономии энергии.
На уровне ядра я держу домашние процессоры отдельно от латентных ядер, минимизирую прерывания по таймеру на горячих ядрах (nohz_full) и паркую фоновую работу (compaction, Kswapd) предпочтительно на системных ядрах узла, который не работает с латентными путями.
Поиск и устранение неисправностей и типичные анти-паттерны
- СимптомЗадержка P99 увеличивается после развертывания. ПричинаHeaps/Caches first-touch на неправильном узле. РешениеРазминка/предварительное касание под целевой близостью, затем открытый распределитель нагрузки.
- СимптомВысокое время SoftIRQ на „неправильных“ процессорах. Причинаirqbalance, распределенный по узлам. РешениеИсправьте сродство IRQ, установите совместимость с узлами RPS/RFS/XPS.
- СимптомOOM в узле, хотя системная оперативная память свободна. ПричинаСтрогая маска NUMA без буфера. РешениеИсправьте емкость или используйте „предпочтительную“, установите оповещения для каждого узла.
- СимптомНеравномерная пропускная способность при использовании NVMe. ПричинаНеправильное отображение очередей, общие очереди между узлами. Решение: очереди blk-mq/NVMe на узел, потоки ввода-вывода распределены.
Практический контрольный список
- Запись топологии: Узлы, ядра, оперативная память, устройства PCIe на сокет.
- Нарисуйте раздел обслуживания: Какие пути Латентность-Критично, какая партия?
- Установите сродство процессора/памяти для каждого класса; обратите внимание на первое касание при запуске.
- Привяжите IRQ/очереди рядом с узлом; проверьте очереди RSS/RPS/XPS и NVMe.
- Мониторинг на P95/P99, удаленный доступ, очередь выполнения, распределение IRQ.
- Специально контролируйте автобалансировку; выберите THP/zone_reclaim_mode соответствующим образом.
- Поддерживайте согласованность vNUMA, vCPU pinning и vRAM binding в виртуальных машинах/контейнерах.
- Тестируйте итеративно, документируйте, откатывайтесь назад в случае отклонения и дорабатывайте.
Краткое содержание и график настройки
Это приносит наибольшую отдачу, Темы и память вместе, сократить пути ввода-вывода и распределять их только осторожно. Я начинаю с анализа топологии, планирую обслуживание узла за узлом, устанавливаю сродство процессора и памяти, подключаю сеть/хранилище соответствующим образом и отслеживаю значения P95/ P99 с акцентом на Удаленный-доступа. Затем я настраиваю размеры пулов, маски IRQ и политики, пока пики задержки не исчезнут, а пропускная способность не увеличится. Для виртуальных машин и контейнеров я проверяю размещение отдельно, поскольку гипервизор оказывает большое влияние и Границы работают по-разному. Если вы повторите и задокументируете этот процесс, вы получите ощутимо большую производительность от Server NUMA Locality и CPU-Memory Affinity - зачастую дешевле, чем обновление дополнительного оборудования в евро.


