Servidor NUMA La localidad y la afinidad de memoria de la CPU determinan lo cerca que trabajan los hilos de su RAM y lo constantes que permanecen las latencias en las pilas de alojamiento. Te mostraré de forma práctica cómo puedes conseguir un rendimiento mediblemente mayor con el reconocimiento de topologías, estrategias de afinidad y rutas de E/S cercanas al nodo y... Latencia notablemente inferior.
Puntos centrales
Para una orientación rápida, resumo los mensajes clave antes de explicar los pasos en detalle y respaldarlos con ejemplos, para que pueda ver inmediatamente por dónde debe empezar para Localidad y Affinity de forma rentable. Hago hincapié en las relaciones claras entre hilos, memoria y E/S para que pueda derivar prioridades de forma limpia y Decisiones cumplir. También identifico escenarios en los que Interleave tiene sentido sin diluir sus rutas críticas y muestro cómo puede demostrar un progreso real a través de la supervisión y Error se evitan. Para los entornos virtualizados, proporciono consejos sobre la colocación de vCPUs y vRAM para que los sistemas invitados no se deslicen a través de múltiples nodos y Remoto-los accesos explotan. Por último, plasmaré las conclusiones en una breve hoja de ruta para que puedas proceder de forma estructurada y dar cada paso en la dirección correcta. medible seguro.
- Localidad primero: mantén los hilos cerca de tu propia RAM, evita los remotos.
- Afinidad corregir: Agrupación de núcleos y memoria por política.
- Topología leer: Nodos, núcleos, dispositivos PCIe por socket.
- Vías de E/S paquete: Acoplar NIC, NVMe y app en el mismo nodo.
- ferias en lugar de adivinar: P95/ P99, acceso remoto y seguimiento del rendimiento.
Comprender la topología NUMA
Antes de trasladar cargas de trabajo, leo el Topología del servidor: cuántos nodos NUMA existen, cuántos núcleos y cuánta RAM están conectados a cada nodo. También presto atención a qué dispositivos PCIe -como las NIC o las SSD NVMe- están conectados a cada zócalo, ya que esto determina las rutas de interrupción y los accesos a la memoria, y cuánta RAM hay conectada a cada nodo. Latencia caracterizado. Un nodo proporciona acceso local a la memoria con una distancia corta; todo lo que vaya más allá cuesta tiempo y ancho de banda. Cuanto más grande es la máquina, con múltiples sockets, más afecta el acceso remoto a los tiempos de respuesta y se come el ancho de banda. Rendimiento. Para una introducción comprensible a la lógica de hardware, encuentro un compacto Los nodos NUMA de un vistazo, considerar conscientemente los límites de los nodos y evitar distribuciones incorrectas.
En la práctica, empiezo con un breve inventario topológico y lo documento para poder derivar después las decisiones de afinidad de forma comprensible. Comandos útiles:
Núcleos # y asignación NUMA
lscpu -e=CPU,Core,Socket,Node
Visión general del hardware # NUMA
numactl --hardware
# Asignar dispositivos PCIe a su nodo NUMA
lspci -nn | grep -E "Ethernet|No volátil"
for d in /sys/bus/pci/devices/*; do echo -n "$d: "; cat $d/numa_node; done
Lo importante es que Complejo raíz PCIe y ranuras de dispositivos a los zócalos. Dos puertos de la misma NIC se pueden asignar a nodos diferentes; esto influye en la ubicación de las colas RX/TX y las IRQ. Lo mismo se aplica a NVMe: los controladores modernos tienen varias colas que deberías vincular a núcleos cercanos al nodo para que DMA no desencadene ningún salto de nodo.
Utilizar correctamente la afinidad de memoria de la CPU
Con la afinidad CPU-memoria, vinculo firmemente los procesos a las zonas centrales e impongo la asignación de memoria local en la medida de lo posible, de modo que Hilos no alcancen constantemente el borde del nodo. En Linux, defino las CPU a través de systemd o cgroups y combino esto con políticas de memoria para que la RAM se cree preferiblemente en el mismo nodo y Remoto se reduce al mínimo. Los servicios críticos (frontales de API, cachés en memoria, bases de datos) se benefician de inmediato porque se reducen los tiempos de espera del controlador de memoria y los accesos a la caché son más frecuentes. Sin embargo, los límites de fijación demasiado estrictos pueden restringir la programación, por lo que respaldo cada ajuste con pruebas de rendimiento y controlo los valores P95/P99 para detectar efectos notables en Usuario-experiencia. Una introducción compacta a Affinity en el alojamiento le ayudará a empezar: Conocimiento de afinidad y NUMA proporcionar las herramientas necesarias para una colocación limpia.
El factor decisivo es la Principio de primera intervenciónLa memoria se crea en el nodo que escribe primero en la página. Por lo tanto, inicializa grandes heaps o buffers en los núcleos de destino del nodo en el que el servicio se ejecutará más tarde - idealmente con la política de CPU y memoria ya establecida (por ejemplo, a través de systemd unit o numactl). Si arrancas en frío en el nodo 0 y luego mueves los hilos al nodo 1, la mayoría de las páginas permanecerán remotas. Para pilas de tiempos de ejecución grandes, vale la pena usar „pre-touch“ durante el arranque para que las páginas se roten localmente y luego permanezcan calientes.
Conocimiento de NUMA en la pila de alojamiento
Un sistema operativo compatible con NUMA, un hipervisor adecuado y aplicaciones con thread pinning despliegan juntos todo su potencial. Posible. El SO favorece la colocación local si hay recursos libres en el nodo, mientras que el hipervisor asigna las máquinas virtuales de forma que las vCPU y la vRAM no se distancien y Localidad se mantiene. En la aplicación, separo los pools de trabajadores por nodo y mantengo las colas locales en lugar de operar pools globales de forma cruzada. Organizo los procesos de la base de datos, los demonios de caché y las instancias del servidor web nodo por nodo, para que las rutas de acceso sean cortas y se mantengan seguras. Jitter disminuye. Esto aumenta la coherencia y la previsibilidad bajo carga, lo que influye directamente en la previsibilidad de los SLA en euros y ahorra el costoso sobreaprovisionamiento.
En el nivel Ingress, me ocupo de Afinidad de nodos de las sesiones, por ejemplo mediante un enrutamiento pegajoso o un hash coherente (por ejemplo, en la IP del cliente o en los tokens de sesión), para que las peticiones acaben en „su“ trabajador y caché locales. Para los servicios con estado, planifico réplicas por nodo y equilibro el acceso de lectura localmente; igualo las rutas de escritura mediante replicación asíncrona o por lotes para evitar el ping-pong entre nodos.
Programar servicios nodo por nodo
Agrupo las capas de una pila de forma que cada capa tenga una referencia de nodo clara y Caminos quedarse corto. Una separación clásica: web/API por nodo, app worker a su lado, más la caché local; la base de datos también se sitúa cerca del nodo si la huella de RAM cabe y IO-path no se interrumpe. Muevo los trabajos de generación de informes, copias de seguridad o batch workers a nodos menos críticos para que las peticiones interactivas no se vean afectadas. Evito las instancias monolito de gran tamaño porque a menudo cruzan los límites de los nodos y, por tanto, generan carga remota, que Actuación borrosa. Las instancias más pequeñas y replicadas por nodo suelen ofrecer un mejor rendimiento en el uso diario, ya que respetan las reglas NUMA y suavizan los picos.
Para planificar la capacidad, calculo Espacio libre por separado para cada nodo: buffer de CPU para ráfagas, buffer de RAM contra OOM y márgenes separados para la caché de páginas. De este modo, evito que el núcleo conmute involuntariamente de forma remota. Defino rutas de conmutación claras para la conmutación por error: si falla un nodo, las instancias de reemplazo pueden ejecutarse entre nodos, pero limito su concurrencia hasta que se restaura el nodo original, lo que mantiene estable la latencia global.
Configuración de la afinidad de la CPU: Métodos y dificultades
Para la asignación de núcleos, utilizo systemd con CPUAffinity o cgroups con cpuset.cpus, de modo que los servicios han fijado Áreas principales recibir. A la hora de pinear, presto atención a los pares de hyper-threading, ya que dos hilos lógicos de una unidad física comparten recursos y pueden ralentizarse mutuamente si los combino de forma infeliz, y Consejos crear. Las rutas de latencia -terminación de TLS, entrada de API, lectores de caché- obtienen núcleos exclusivos, mientras que los registros, la compresión o las copias de seguridad se mueven a otros pools. Los pools demasiado estrechos sin búferes provocan colas, por lo que tengo en cuenta el espacio libre y compruebo los cambios de contexto, la longitud de las colas de espera y la disponibilidad de los recursos. IRQ-distribución. De la observación deduzco si abro más los núcleos o los concentro más hasta que la distribución de la latencia cae limpiamente y los picos P99 se vuelven más silenciosos.
Para reducir aún más el jitter, configuro selectivamente interruptores del kernel como nohz_full y rcu_nocbs para núcleos de latencia exclusiva, aíslalos de los servicios del sistema y coloca deliberadamente IRQs sólo en CPUs destinadas a este fin. Utilizo el servicio „irqbalance“ con precaución: configúralo específicamente o desactívalo si está frustrando tu afinidad IRQ manual. Utilizo SCHED_FIFO/SCHED_RR con moderación y sólo con límites Be para evitar la inversión de prioridades o la inanición.
Políticas de memoria y máscaras NUMA
Para la política de memoria, diferencio entre asignación local preferente, intercalación entre varios nodos y máscaras NUMA fijas mediante cpuset.mems, de modo que RAM fluye hacia donde se están ejecutando realmente los hilos. Para los servicios interactivos, suelo establecer „preferido“, lo que significa que el sistema asigna localmente y sólo se desvía cuando hay escasez, que es Remoto-los accesos son limitados. Los trabajos analíticos o de streaming a veces se benefician del intercalado porque el ancho de banda se distribuye entre nodos y se reduce la presión sobre un controlador. Las máscaras fijas ofrecen control, pero requieren disciplina en la planificación de la capacidad para que no se produzcan eventos OOM no deseados en un nodo que suban y Servicios interferir. La siguiente tabla clasifica las políticas más comunes en escenarios típicos y le ayuda a tomar una decisión rápida.
| Política | Efecto | Cargas de trabajo típicas | Riesgo |
|---|---|---|---|
| Preferido (local) | RAM principalmente en el nodo local, opción alternativa en caso de escasez | Web/ API, cachés, bases de datos OLTP | Ligera deriva a plena carga en otros nodos |
| Intercalar | Distribución uniforme en los nodos seleccionados | Streaming, análisis, grandes exploraciones | Mayor latencia en los accesos individuales |
| Máscara NUMA fija | Vinculación estricta a los nodos de memoria definidos | Servicios estrictamente encapsulados, pruebas deterministas | Riesgo de OOM si el presupuesto se planifica incorrectamente |
Vigila los interruptores de todo el sistema: zone_reclaim_mode influye en si un nodo limpia agresivamente su propia memoria antes de asignar remotamente - a menudo indeseable para rutas de latencia. Páginas enormes transparentes (THP) puede desencadenar la migración de páginas o generar atascos; para los servicios sensibles a la latencia suelo elegir „madvise“ y utilizar hugepages estáticos cuando tiene sentido, de modo que aumenten los accesos a la TLB y disminuyan los picos de fallos de página.
Vincular rutas de red y E/S cerca del nodo
Alineo las colas de las NIC (RX/TX) para que sus IRQ apunten a los núcleos del nodo apropiado y el procesamiento de paquetes tenga lugar donde el App computa. Lo mismo se aplica a las unidades SSD NVMe o a los controladores RAID: los subprocesos de E/S deben ejecutarse en el nodo al que está conectado el dispositivo a través de PCIe, para que las rutas DMA sigan siendo cortas y el dispositivo pueda utilizarse de forma más eficiente. Cuellos de botella no se materializan. En Linux, ajusto las máscaras de afinidad IRQ y las vinculo a los grupos de CPU de mis servicios para crear una ruta continua. Con microrráfagas de la red, como muchos apretones de manos TLS, esta proximidad compensa directamente porque las rutas de copia son más cortas y las cachés de CPU permanecen calientes y Contexto con menos frecuencia. El resultado es un flujo de datos coherente desde el paquete a la aplicación y a la memoria, sin saltos de nodo innecesarios.
Palancas concretas en la pila de la red: RSS para la distribución de hardware a las colas, RPS/RFS para el control de la CPU por software y XPS para la selección de TX. Yo utilizo ethtool para asignar colas de RX a grupos de núcleos que se ejecutan en el mismo nodo que sus trabajadores. Para el almacenamiento utilizo blk-mq-ajuste y asignación de colas por nodo; las controladoras NVMe ofrecen varias colas de envío/compleción, que yo escalo y afino ≤ número de núcleos por nodo. Compruebe regularmente si las interrupciones (cat /proc/interrupts) se están disparando donde se encuentran los núcleos de su aplicación; puede reconocer la deriva por el aumento de bytes remotos a pesar de una carga estable.
Estructurar la arquitectura de la aplicación de acuerdo con NUMA
A nivel de aplicación, configuro mis propios grupos de trabajadores para cada nodo NUMA, mantengo las colas locales y evito los puntos calientes de bloqueo global para que Hilos no saltar de un lado a otro. Configuro la fragmentación de sesiones y datos para que las particiones calientes permanezcan donde se ejecutan los trabajadores solicitantes y Tiempo no se pierde en el tráfico entre nodos. Para las cachés, a menudo utilizo réplicas en lugar de una instancia central para que los lectores obtengan copias locales de nodo. En los clientes Netty, Tokio, libuv o DB, anclo los bucles de eventos a núcleos fijos y presto atención a la proximidad de IRQ para que los cambios de tarea sean limitados y Cachés golpear mejor. Esta disposición reduce los efectos de ping-pong y hace que los tiempos de respuesta sean más constantes a lo largo del día.
Una palanca subestimada es Asignador y opciones de ejecución: Los asignadores habilitados para NUMA (jemalloc/tcmalloc) reducen la contención entre hilos y mantienen las páginas más cerca de los núcleos de origen de los hilos. En las pilas JVM, las opciones como NUMA awareness y pre-touch ayudan a determinar las fases de fallo; en .NET, alineo los hilos de GC cerca de los nodos y presto atención a la GC del servidor para suavizar los tiempos de parada. En Go, dimensiono los GOMAXPROCS por pool de nodos y mantengo los planificadores de goroutine alejados de los núcleos de latencia que trabajan cerca de IRQ.
Control sensible del autoequilibrio NUMA
Los mecanismos automáticos de equilibrio NUMA del núcleo pueden ayudar a suavizar la carga distribuida, pero siempre compruebo si son capaces de cumplir mis Afinidad se ven perjudicados. En los servicios de latencia crítica, deshabilito o reduzco el movimiento automático si saca hilos de su memoria local y Consejos generado. Para trabajos analíticos o procesamiento por lotes amplios, tiendo a dejar activado el equilibrado porque puede aumentar el ancho de banda sin degradar la interacción. Una introducción práctica a las estrategias de equilibrado me proporciona puntos de partida adicionales: Comprender el equilibrio NUMA muestra cuándo debe llevar el sistema automático y cuándo debe asignarse manualmente. Al final, tomo una decisión basada en datos para cada clase de servicio en lugar de adoptar a ciegas una configuración global por defecto y Objetivos para fallar.
Cuando se activa el equilibrado, controlo las tasas de migración, los picos de fallos menores y mayores y el robo de CPU por nodo. Si las páginas se mueven de un lado a otro cíclicamente, lo contrarresto con un pinning más estricto, pre-touch y máscaras de memoria más estrechas. En cambio, en las cargas de trabajo con exploraciones largas y secuenciales, el equilibrado puede armonizar la carga, siempre que no se vean afectadas las rutas de latencia interactiva.
Seguimiento: medir, comparar, decidir
Sin mediciones, el ajuste sigue siendo un juego de adivinanzas, así que hago un seguimiento de la carga de la CPU por núcleo y por nodo, la utilización de la memoria por nodo y la proporción de Remoto-accesos. Para la experiencia del usuario, las latencias P95/P99 cuentan mucho más que los valores medios, porque los valores atípicos caracterizan la impresión de SLA y Costos hacia arriba. Ejecuto perfiles de carga realistas con cachés frías y calientes porque ambos mundos muestran diferentes cuellos de botella. Después de cada cambio, documento los ajustes, la fecha de la prueba y los resultados para poder revertir con seguridad las modificaciones más adelante y Conocimiento no se pierde. Si además se correlacionan las métricas de la aplicación (longitud de las colas, reintentos, recogida de basuras) con los valores del sistema, se puede reconocer más rápidamente la causa y el efecto.
Ayuda práctica en el análisis:
- numastat (relacionados con el sistema y el proceso) para local vs. Remoto-Hit
- /proc/interrupts y SoftIRQ tiempo por CPU para IRQ drift
- eventos perf y estadísticas del planificador para la profundidad de la cola de ejecución, cambios de contexto, fallos de LLC, etc.
- fio/iperf/wrk con grupos de trabajadores específicos de cada nodo para realizar comparaciones reproducibles.
La evaluación se hace por nodo: Espero que los histogramas de latencia estén próximos entre sí. Si un nodo se mueve hacia arriba, primero busco una carga IRQ mal distribuida, deriva en la caché de páginas o heaps que se asignaron al nodo equivocado durante el calentamiento.
NUMA en máquinas virtuales y contenedores
En virtualización, la colocación de vCPUs y vRAM en un nodo compartido es importante para que las cargas de trabajo de los invitados no se deshilachen y Latencia tira para arriba. Dimensiono la RAM para que quepa en el nodo local y evito las grandes máquinas virtuales que se extienden por varios nodos y Deriva disparador. Para los contenedores, utilizo controladores cpuset para que los grupos de pods funcionen de forma consistente en un nodo y el almacenamiento se cree localmente. Prefiero colocar a los huéspedes de E/S pesada en el nodo con una conexión de almacenamiento directa para mantener las rutas DMA cortas y IRQ-reducir el ruido. Esto significa que incluso los hosts de virtualización densos siguen siendo predecibles y llevan más proyectos en el mismo hardware.
Presto atención a vNUMA-Exposición: El huésped debe ver la misma estructura de nodos que el hipervisor proporciona físicamente. vCPU pinning y vRAM binding van de la mano; muevo hot-adds durante ventanas de mantenimiento si es posible, porque de lo contrario las nuevas páginas acaban remotamente. En Kubernetes, configuro la QoS „garantizada“, el gestor de CPU „estático“ y la colocación según la topología para que los pods no se muevan entre nodos. Para SR-IOV/VFs, asigno VFs al nodo físico apropiado y enlazo las colas IRQ a los conjuntos de CPUs de los pods o VMs a los que sirven.
Preparación específica del primer toque, calentamiento y amontonamiento
Muchos errores de rendimiento se producen durante InicioLos montones crecen en la fase de calentamiento, cuando llegan las primeras peticiones, a menudo en el centro de un nodo. Por tanto, ejecuto calentamientos controlados para cada nodo: inicio instancias con una máscara de CPU/memoria establecida, ejecuto consultas de precarga específicas e inicializo cachés en paralelo para cada nodo. Para los servicios de la JVM, activo la precarga de la pila; para las bases de datos, segmento los buffer pools nodo por nodo. Esto reduce las migraciones de página posteriores y garantiza que las primeras peticiones no caractericen aleatoriamente la distribución de la memoria.
Ajuste del kernel/BIOS para latencias constantes
Bajo el capó, ajusto la potencia y la política de interrupción:
- Ajuste el regulador de la CPU a „rendimiento“, limite los estados C profundos, utilice los estados C del paquete con cuidado para Jitter reducir.
- No estrangular la frecuencia de la memoria; los perfiles energéticos equilibrados suelen minimizar Rendimiento bajo carga.
- Evite la modulación de espectro ensanchado/reloj si la coherencia es más importante que un ahorro mínimo de energía.
A nivel de kernel, mantengo las CPUs de mantenimiento separadas de los núcleos de latencia, minimizo las interrupciones del temporizador en los núcleos calientes (nohz_full) y aparco el trabajo en segundo plano (compactación, Kswapd) preferiblemente en los núcleos de sistema de un nodo que no ejecuta rutas de latencia.
Resolución de problemas y antipatrones típicos
- SíntomaLa latencia de P99 salta después de los despliegues. CausaHeaps/Caches first-touch en nodo erróneo. SoluciónCalentamiento/pre-toque bajo afinidad objetivo, luego abrir distribuidor de carga.
- SíntomaAlto tiempo de SoftIRQ en CPUs „erróneas“. Causairqbalance distribuido por los nodos. SoluciónFijar la afinidad IRQ, establecer RPS/RFS/XPS compatible con el nodo.
- SíntomaOOM en un nodo, aunque la RAM del sistema esté libre. CausaMáscara NUMA estricta sin búfer. SoluciónCorregir capacidad o usar „preferente“, establecer alertas por nodo.
- SíntomaRendimiento irregular con NVMe. CausaAsignación incorrecta de colas, colas compartidas entre nodos. Solucióncolas blk-mq/NVMe por nodo, hilos de E/S fijados.
Lista de control práctica
- Registre la topología: Nodos, núcleos, RAM, dispositivos PCIe por socket.
- Dibuje la sección de servicio: ¿Qué caminos son Latencia-crítico, ¿qué lote?
- Establezca la afinidad CPU/memoria para cada clase; anote el primer toque al inicio.
- Vincule IRQ/colas cerca del nodo; compruebe las colas RSS/RPS/XPS y NVMe.
- Monitorización en P95/P99, acceso remoto, cola de ejecución, distribución IRQ.
- Controle el autoequilibrio de forma selectiva; seleccione THP/zone_reclaim_mode adecuadamente.
- Mantenga vNUMA, vCPU pinning y vRAM binding consistentes en VMs/contenedores.
- Pruebe de forma iterativa, documente, retroceda en caso de desviación y ajuste.
Breve resumen y calendario de puesta a punto
Aporta el mayor rendimiento, Hilos y memoria juntos, acortar las rutas de E/S y distribuirlas sólo con cuidado. Empiezo con un análisis de la topología, planifico los servicios nodo por nodo, establezco la afinidad de CPU y memoria, conecto la red/almacenamiento adecuadamente y controlo los valores P95/ P99 centrándome en Remoto-accesos. A continuación, ajusto el tamaño de los grupos, las máscaras IRQ y las políticas hasta que los picos de latencia disminuyen y el rendimiento aumenta. Para las máquinas virtuales y los contenedores, compruebo la colocación por separado porque el hipervisor tiene mucha influencia y Límites funcionan de forma diferente. Si repite y documenta este proceso, obtendrá un rendimiento mensurablemente mayor de la localidad NUMA del servidor y de la afinidad CPU-memoria, a menudo más barato que actualizar el hardware adicional en euros.


