Algunos problemas de rendimiento se sienten personales. Compraste un Ryzen o EPYC con muchos núcleos, le diste NVMe rápidos, le pusiste suficiente RAM para avergonzar al clúster de ayer, y aun así se comporta como si arrastrara un piano por las escaleras. Las gráficas muestran CPU “disponible”, discos “bien”, red “bien”, y aun así la carga va lenta y con jitter.
Normalmente es entonces cuando aparece Infinity Fabric. No como un componente al que puedas señalar con un destornillador, sino como la interconexión que decide si tus chiplets cooperan como un equipo bien entrenado o discuten como en una revisión de cambios a las 16:55.
Qué es realmente Infinity Fabric (y qué no es)
Infinity Fabric es la arquitectura de interconexión escalable de AMD: la plomería que mueve datos y tráfico de coherencia entre núcleos de CPU, cachés, controladores de memoria y I/O. En los Ryzen y EPYC modernos, es el pegamento que hace que el enfoque por chiplets se comporte como una CPU lógica única (la mayor parte del tiempo).
Si vienes de la era de die monolíticos, piensa en esto así: la “CPU” ahora es un pequeño campus. Tienes varios edificios (chiplets de núcleo), un edificio de servicios central (I/O die o controladores de memoria según la generación) y una red entre ellos. Infinity Fabric es esa red interna: switches, enlaces, protocolos, arbitraje y sincronización. Cuando funciona bien, no la notas. Cuando no, la latencia sube y el rendimiento es irregular.
Qué no es: un único reloj, un único bus, o una perilla mágica de rendimiento que giras a “rápido”. Es un sistema por capas con distintos dominios: core-to-core, core-to-memory, core-to-I/O y tráfico de coherencia. La parte más relevante para operaciones es que ciertos caminos de latencia lo atraviesan, y su velocidad efectiva depende de cómo configures la memoria, relojes, la política NUMA y ajustes de BIOS/potencia.
Por qué deberían importarle a operaciones e ingenieros de rendimiento
Porque la mayoría de cargas de producción no son “CPU-bound” en el sentido en que lo muestran los benchmarks. Son sensibles a la latencia. Son sensibles a fallos de caché. Hablan mucho entre hilos. Son sensibles a NUMA. Son una pila de microservicios complicándose entre sí. Y Infinity Fabric forma parte de la historia de latencia siempre que los datos tienen que moverse entre chiplets, regiones de memoria o rutas de I/O.
Aquí es donde los equipos se confunden: la utilización media de CPU parece buena, pero la latencia en las colas se vuelve horrible. O un nodo de base de datos rinde bien en un socket y peor en otro nodo “idéntico”. O un servidor de almacenamiento con todo NVMe consigue menos rendimiento que lo que sugiere la hoja de especificaciones. La fabric no aparece en la mayoría de dashboards, pero tiene voto decisivo.
Broma #1 (corta y dolorosamente precisa): Infinity Fabric es como el Wi‑Fi de la oficina: nadie lo presupuestó, todos lo culpan y de alguna manera siempre está involucrado.
Hechos e historia que realmente importan en 2026
Aquí hay datos concretos que cambian cómo investigas:
- Infinity Fabric llegó como interconexión unificadora en la era Zen para escalar núcleos y I/O sin dies monolíticos. Ese cambio arquitectónico es la razón por la cual “la misma familia de CPU” puede comportarse de forma muy diferente entre generaciones y SKUs.
- Los chiplets convirtieron la latencia de interconexión en un factor de rendimiento de primera clase. En los dies monolíticos, la latencia entre núcleos era mayormente “on-die”. Con chiplets, parte del tráfico ahora debe saltar por la fabric, y tu carga lo tolera —o no—.
- En muchas generaciones de Ryzen, el reloj de la fabric (FCLK) y el reloj de memoria (MCLK) estaban acoplados para obtener la mejor latencia. Desacoplarlos puede permitir frecuencias de memoria más altas, pero añade latencia que castiga cargas sensibles a colas.
- EPYC escaló añadiendo CCDs y un gran I/O die. Eso mejora el rendimiento y el número de núcleos, pero también crea topología: algunos núcleos están “más cerca” de cierta memoria y I/O que otros.
- NUMA no es opcional en sistemas EPYC multi-CCD. Puedes fingir que es UMA, pero el hardware no colabora. El scheduler de Linux intentará compensar, pero no puede leer tu mente ni la localidad de caché.
- Los valores por defecto del BIOS suelen optimizar energía/temperatura, no latencia determinista. Si ejecutas bases de datos, sistemas de trading o targets de almacenamiento, “eficiente en energía” puede significar silenciosamente “jitter”.
- La virtualización magnifica los errores de topología. Una VM que abarca nodos NUMA puede convertir una buena CPU en un generador de memoria remota con un negocio secundario en paquetes perdidos.
- Revisiones de firmware y AGESA han cambiado históricamente el comportamiento de memoria/fabric—a veces mejorando estabilidad, otras cambiando características de rendimiento. “Mismo hardware” antes y después de una actualización puede no ser el mismo sistema.
Un modelo mental: presupuestos de latencia y patrones de tráfico
Cuando la gente dice “cuello de botella de Infinity Fabric”, a menudo se refieren a una de tres cosas:
- Latencia añadida: las solicitudes cruzan límites de chiplet o dominios NUMA con más frecuencia de la esperada, alargando la ruta crítica para fallos de caché, locks y código con alta IPC.
- Ancho de banda limitado: muchos núcleos generan suficiente tráfico de memoria como para saturar la interconexión y los controladores de memoria, provocando contención y colas.
- Jitter: estados de energía, cambios de reloj o contención crean tiempos de servicio inconsistentes; las medias parecen bien, pero el p99 parece una escena del crimen.
En la práctica, la mayoría de incidentes son una mezcla: una carga se vuelve dependiente de memoria remota (latencia) y experimenta contención en la interconexión (ancho de banda) y sufre picos por migraciones del scheduler (jitter). Tu trabajo es identificar cuál domina y escoger la solución de menor riesgo.
Piénsalo en términos de patrones de tráfico:
- Aplicaciones multihilo charlatanas (locks compartidos, colas compartidas, pausas de GC) sufren cuando los hilos saltan entre CCDs/nodos NUMA.
- Bases de datos en memoria se preocupan por latencia de memoria y accesos predecibles. Memoria remota transforma “RAM rápida” en “RAM más lenta con pasos extra”.
- Targets de almacenamiento se preocupan por I/O y localidad de interrupciones. Mala afinidad hace que tus interrupciones NVMe se procesen en núcleos lejanos al root de PCIe, añadiendo latencia y robando caché.
- Anfitriones de virtualización se preocupan por el placement. Un host puede funcionar perfecto hasta que una VM “ruidosa” se extiende por nodos y se convierte en una prueba de estrés para la fabric.
FCLK/UCLK/MCLK y el “impuesto de sincronización”
En muchas plataformas AMD encontrarás tres relojes que importan para memoria y comportamiento de la fabric:
- MCLK: reloj de memoria (relacionado con la tasa de datos DDR).
- UCLK: reloj del controlador de memoria.
- FCLK: reloj de la fabric.
Según la generación y opciones de BIOS, estos pueden correr 1:1 o en ratios (a menudo 1:2) cuando empujas la frecuencia de memoria alta. La trampa es que tasas DDR más altas pueden parecer “más ancho de banda”, mientras que el cambio de ratio añade latencia que castiga exactamente las cargas que te importan en producción.
Aquí está la traducción práctica para operaciones:
- Si tu carga es sensible a la latencia (bases de datos, caches, servicios RPC), normalmente prefieres una configuración estable que mantenga fabric/relojes de memoria en una relación de baja latencia, aunque el ancho de banda pico sea ligeramente inferior.
- Si tu carga es limitada por ancho de banda/streaming (cierta analítica, escaneos secuenciales grandes), aumentar el ancho de banda puede ayudar—hasta que golpees contención en otra parte.
El “impuesto de sincronización” aparece como peor latencia de acceso a memoria, no necesariamente menor ancho de banda medido. Por eso puedes “optimizar” la velocidad DDR y luego preguntarte por qué empeoró el p99. No hiciste la memoria más rápida. Hiciste el sistema menos predecible.
NUMA, chiplets, CCD/CCX: a dónde viajan tus ciclos
En EPYC especialmente, la topología es destino. Los núcleos se agrupan en CCDs (core chiplet dies), y estos se conectan a un I/O die que aloja controladores de memoria y PCIe. Cada CCD tiene su propio L3; el acceso entre CCDs es inherentemente más caro que permanecer local.
NUMA expone esto como múltiples nodos. Incluso en un único socket físico, podrías tener múltiples nodos NUMA según ajustes de BIOS (modos NPS, mapeo de CCD). Linux entonces intenta programar hilos y asignar memoria para reducir accesos remotos. La palabra es “intenta”. Tu carga, el pinning y las políticas de cgroup pueden deshacer esos intentos en segundos.
Patrones comunes que muerden:
- Migración de hilos: los hilos saltan entre núcleos/nodos NUMA por decisiones del scheduler, robando localidad de caché e incrementando accesos a memoria remota.
- Memoria asignada en el nodo equivocado: un proceso arranca en un nodo, aloja memoria allí y luego se programa en otro. Ahora cada acceso a memoria es un viaje por la fabric.
- Interrupciones en los núcleos equivocados: interrupciones NIC/NVMe manejadas en núcleos distantes causan mayor latencia de I/O y ciclos CPU desperdiciados.
PCIe e I/O: la otra mitad de la historia
Infinity Fabric no es solo “CPU-a-RAM”. También forma parte de cómo se atiende el I/O. En muchos sistemas, los dispositivos PCIe cuelgan de root complexes asociados a ciertos nodos NUMA (o al menos a ciertos dominios de localidad). Si el manejo de interrupciones del almacenamiento y su procesamiento ocurren “lejos” del camino PCIe, pagas en latencia extra y sobrecarga CPU.
Aquí es donde ingeniería de almacenamiento se encuentra con topología de CPU. Tu RAID NVMe, ZFS, target SPDK o Ceph OSD puede ser técnicamente “rápido”, pero si sus hilos más calientes se ejecutan en núcleos remotos y sus asignaciones de memoria caen en el nodo NUMA equivocado, estás construyendo un sistema de almacenamiento de baja latencia y luego lo estás enrutando por la ruta panorámica.
Tareas prácticas: 12+ comandos, qué significan, qué decides
Estas son tareas reales que puedes ejecutar en un host Linux. No imprimirán mágicamente “Infinity Fabric está triste”, pero te dirán si la topología, la localidad de memoria, los relojes o las interrupciones te están saboteando.
Task 1: Identify CPU model and basic topology
cr0x@server:~$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
CPU(s): 128
Thread(s) per core: 2
Core(s) per socket: 64
Socket(s): 1
NUMA node(s): 4
Model name: AMD EPYC 7xx3 64-Core Processor
NUMA node0 CPU(s): 0-31
NUMA node1 CPU(s): 32-63
NUMA node2 CPU(s): 64-95
NUMA node3 CPU(s): 96-127
Qué significa: Tienes cuatro nodos NUMA en un socket. Esa ya es tu etiqueta de advertencia: la localidad de memoria importa.
Decisión: Si ejecutas cargas sensibles a la latencia, planifica fijar procesos/VMs por nodo NUMA y alinear las asignaciones de memoria. Si asumías UMA, detente.
Task 2: Show detailed NUMA distances
cr0x@server:~$ numactl --hardware
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 0 size: 128798 MB
node 0 free: 97211 MB
node 1 cpus: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
node 1 size: 128829 MB
node 1 free: 99102 MB
node 2 cpus: 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 2 size: 128813 MB
node 2 free: 100118 MB
node 3 cpus: 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
node 3 size: 128820 MB
node 3 free: 99654 MB
node distances:
node 0 1 2 3
0: 10 16 16 16
1: 16 10 16 16
2: 16 16 10 16
3: 16 16 16 10
Qué significa: Distancia local 10, remota 16. Esa es una diferencia de latencia significativa para memoria caliente.
Decisión: Para bases de datos y targets de almacenamiento, trata cada nodo NUMA como un “mini-socket”. Mantén hilos calientes y su memoria en el mismo nodo siempre que sea posible.
Task 3: Visualize topology with hwloc
cr0x@server:~$ lstopo-no-graphics --no-io
Machine (512GB total)
NUMANode L#0 (P#0 128GB)
Package L#0
L3 L#0 (32MB)
L2 L#0 (512KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0 + PU L#0 (P#0)
...
NUMANode L#1 (P#1 128GB)
Package L#0
L3 L#1 (32MB)
...
Qué significa: Puedes ver cómo los núcleos se mapean a rebanadas L3 y nodos NUMA. Este es tu plano de colocación.
Decisión: Usa esto para elegir conjuntos de CPU para servicios e interpretar “¿por qué migró ese hilo?” más adelante.
Task 4: Check kernel NUMA balancing status
cr0x@server:~$ cat /proc/sys/kernel/numa_balancing
1
Qué significa: El balanceo NUMA automático está activado. Puede ayudar a cargas genéricas, y también puede crear migraciones de páginas (jitter) para servicios críticos de latencia.
Decisión: Para cargas sensibles a latencia y con pinning, considera deshabilitarlo a nivel del sistema y gestionar la colocación explícitamente—o al menos prueba ambas opciones. No adivines.
Task 5: Observe NUMA placement and memory policy for a running process
cr0x@server:~$ pidof postgres
2147
cr0x@server:~$ numactl --show --pid 2147
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0
nodebind: 0
membind: 0
Qué significa: Este proceso está efectivamente confinado al nodo 0 para CPU y memoria. Eso es bueno—si la carga cabe en el ancho de banda y caché del nodo 0.
Decisión: Si el proceso es grande y limitado por ancho de banda, podrías escalar partiéndolo entre nodos. Si es sensible a latencia, mantenlo compacto y local.
Task 6: Measure remote vs local memory access tendencies with numastat
cr0x@server:~$ numastat -p 2147
Per-node process memory usage (in MBs) for PID 2147 (postgres)
Node 0 56321.4
Node 1 112.7
Node 2 95.3
Node 3 88.9
Total 56618.3
Qué significa: La memoria está mayormente en el nodo 0. Si los hilos también se ejecutan en el nodo 0, estás en buena forma. Si no, pagas latencia de fabric.
Decisión: Si ves memoria sustancial en nodos no locales, corrige el pinning o la colocación al inicio. Para servicios: arráncalos con numactl o políticas systemd de CPU/memoria.
Task 7: Check for excessive page migrations (a jitter source)
cr0x@server:~$ grep -E 'pgmigrate|numa' /proc/vmstat | head
numa_pte_updates 1829401
numa_huge_pte_updates 0
numa_hint_faults 219884
numa_hint_faults_local 171102
numa_pages_migrated 48211
pgmigrate_success 47998
pgmigrate_fail 213
Qué significa: Se están migrando páginas. Alguna migración es normal con balanceo habilitado; muchas migraciones bajo carga es señal de que el scheduler y la política de memoria están peleando con tu carga.
Decisión: Si la latencia p99 se correlaciona con picos de migración, reduce la migración: fija hilos, asigna memoria localmente, considera deshabilitar el balanceo NUMA automático para esa clase de hosts.
Task 8: Verify CPU frequency governor (latency vs power)
cr0x@server:~$ cpupower frequency-info | sed -n '1,18p'
analyzing CPU 0:
driver: amd-pstate-epp
CPUs which run at the same hardware frequency: 0
hardware limits: 1.50 GHz - 3.70 GHz
available cpufreq governors: performance powersave
current policy: frequency should be within 1.50 GHz and 3.70 GHz.
The governor "powersave" may decide which speed to use
current CPU frequency: 1.74 GHz
Qué significa: Estás en una política “powersave”. Eso está bien para cómputo por lotes, y a menudo es malo para latencia en colas y capacidad de respuesta de I/O.
Decisión: Para nodos críticos de latencia, ajusta el governor a performance (y valida termalidad). Hazlo como una política por rol, no como un remedio puntual.
Task 9: Set performance governor (controlled change)
cr0x@server:~$ sudo cpupower frequency-set -g performance
Setting cpu: 0
Setting cpu: 1
Setting cpu: 2
Setting cpu: 3
Qué significa: Has reducido la variabilidad en el escalado de frecuencia.
Decisión: Vuelve a medir latencia p95/p99 y throughput. Si mejora, incorpóralo en la configuración para esa clase de sistemas.
Task 10: Check PCIe device NUMA locality (critical for NVMe/NIC)
cr0x@server:~$ lspci -nn | grep -E 'Non-Volatile|Ethernet' | head -n 3
01:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]
41:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]
81:00.0 Ethernet controller [0200]: Mellanox Technologies MT28908 Family [ConnectX-6] [15b3:1017]
cr0x@server:~$ cat /sys/bus/pci/devices/0000:01:00.0/numa_node
0
Qué significa: Ese dispositivo NVMe es local al nodo NUMA 0 (o al menos así lo cree el kernel). Si tus hilos de almacenamiento se ejecutan en el nodo 3, has inventado I/O remoto.
Decisión: Alinea el manejo de IRQ y los hilos trabajadores con el nodo NUMA del dispositivo. Para servidores con múltiples dispositivos, considera colas/trabajadores por nodo.
Task 11: Inspect IRQ distribution (interrupt locality)
cr0x@server:~$ grep -E 'nvme|mlx5' /proc/interrupts | head -n 6
47: 12841 11993 12110 11888 PCI-MSI 1048576-edge nvme0q0
48: 42112 39877 40655 39221 PCI-MSI 1048577-edge nvme0q1
92: 11822 12031 11760 11911 PCI-MSI 524288-edge mlx5_comp0
93: 12203 12188 12002 12244 PCI-MSI 524289-edge mlx5_comp1
Qué significa: Los IRQs están aterrizando en un conjunto pequeño de CPUs (aquí los primeros). Puede estar bien; puede ser terrible si esos CPUs no son locales al dispositivo o si están ocupados.
Decisión: Si ves hotspots de IRQ o manejo en nodos equivocados, cambia la afinidad o usa irqbalance con configuración consciente de NUMA. Luego valida latencia.
Task 12: Pin an IRQ to CPUs local to a NUMA node (surgical)
cr0x@server:~$ cat /proc/irq/47/smp_affinity_list
0-3
cr0x@server:~$ sudo sh -c 'echo 0-31 > /proc/irq/47/smp_affinity_list'
cr0x@server:~$ cat /proc/irq/47/smp_affinity_list
0-31
Qué significa: Has ampliado el conjunto objetivo del IRQ a CPUs 0–31 (a menudo nodo 0). Esto mejora la localidad si el dispositivo está en el nodo 0.
Decisión: Vuelve a probar la latencia de I/O bajo carga. Si mejora, estandariza mediante udev/systemd (con cuidado) o ajusta la política de irqbalance en vez de escribir manualmente en /proc.
Task 13: Check memory latency signals with perf (watch for stalled cycles)
cr0x@server:~$ sudo perf stat -e cycles,instructions,cache-misses,LLC-load-misses -p 2147 -- sleep 10
Performance counter stats for process id '2147':
18,421,334,112 cycles
12,102,884,551 instructions # 0.66 insn per cycle
221,433,112 cache-misses
98,331,220 LLC-load-misses
10.001112123 seconds time elapsed
Qué significa: IPC bajo con muchas fallas de última caché puede indicar presión de latencia de memoria—a menudo amplificada por accesos a memoria remota en topologías con mucha fabric.
Decisión: Si el IPC cae bajo carga y sospechas memoria remota, valida con estadísticas NUMA y colocación. No saltes directamente a “upgrade de CPU”.
Task 14: Confirm Transparent Huge Pages status (can interact with migration/latency)
cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
Qué significa: THP está siempre habilitado. A veces eso es bueno; otras veces causa picos de latencia por defrag o comportamiento de asignación, especialmente con cargas mixtas.
Decisión: Para bases de datos con prácticas recomendadas, síguelas. Si no sabes, prueba bajo carga realista; no sigas ciegamente “deshabilitar THP” o “siempre THP”.
Task 15: Check if your process is bouncing across CPUs (migration)
cr0x@server:~$ pidstat -t -p 2147 1 5
Linux 6.8.0 (server) 01/10/2026 _x86_64_ (128 CPU)
12:00:01 PM UID TGID TID %usr %system %CPU CPU Command
12:00:02 PM 26 2147 2147 8.00 2.00 10.00 4 postgres
12:00:02 PM 26 2147 2161 6.00 1.00 7.00 37 postgres
12:00:02 PM 26 2147 2162 5.00 1.00 6.00 92 postgres
Qué significa: Los hilos se están ejecutando en CPUs 4, 37, 92—probablemente diferentes nodos NUMA. Eso no es automáticamente incorrecto, pero es una señal de alarma para una base de datos sensible a latencia.
Decisión: Si el rendimiento es inconsistente, constriñe la BD a un nodo (o a un conjunto de nodos con sharding explícito) y vuelve a probar.
Task 16: Validate memory bandwidth pressure with pcm-like signals (fallback: vmstat)
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 0 0 987654 12345 456789 0 0 2 18 9000 22000 55 10 33 2 0
18 0 0 982110 12345 456792 0 0 0 12 9800 26000 62 12 24 2 0
Qué significa: Altos cambios de contexto y hilos ejecutables pueden indicar contención (locks, scheduling) que empeora cuando los hilos se extienden por nodos NUMA. No es concluyente, pero es una pista.
Decisión: Empareja esto con cheques de colocación NUMA. Si la contención coincide con dispersión inter-nodos, ajusta la afinidad.
Guía de diagnóstico rápido
Esta es la versión “suena la página de guardia”. No tienes tiempo para convertirte en microarquitecto, tienes tiempo para detener la hemorragia.
First: Prove whether it’s locality/topology
- Comprueba el recuento y mapeo NUMA (
lscpu,numactl --hardware). Si NUMA>1, asume que la localidad importa hasta que se demuestre lo contrario. - Comprueba la dispersión de procesos (
pidstat -t,ps -o psr). Si hilos calientes se ejecutan en nodos distantes, sospecha latencia inducida por la fabric. - Comprueba la colocación de memoria (
numastat -p). Si la memoria está mayormente en un nodo pero los hilos corren en otro, encontraste un culpable probable.
Second: Look for jitter sources that amplify fabric costs
- Governor/p-states de CPU (
cpupower frequency-info). “powersave” en nodos críticos de latencia es una victoria fácil (con atención térmica). - Migraciones NUMA (
/proc/vmstatmigraciones y hint faults). Migraciones altas bajo carga suelen correlacionar con picos en las colas. - Comportamiento THP/defrag (estado de THP, guía por carga). No siempre es relacionado con la fabric, pero se acopla con la latencia.
Third: Validate I/O locality (storage and network)
- Nodo NUMA del dispositivo (
/sys/bus/pci/devices/.../numa_node). - Distribución de IRQs por CPU (
/proc/interrupts, ajustes de afinidad). Manejo de IRQ en nodo equivocado es la falla clásica “por qué NVMe es lento”. - Profundidad de cola / moderación de interrupciones (específico del dispositivo, pero empieza confirmando que no estás limitado por un core ocupado manejando todo).
Si solo haces una cosa en la primera hora: haz la carga local—CPU, memoria e interrupciones de I/O alineadas—y vuelve a probar. Es el modo de fallo “modelado por la fabric” más común y el más rápido de corregir.
Errores comunes: síntoma → causa raíz → solución
1) Symptom: Great average throughput, terrible p99 latency
Causa raíz: Migración de hilos y acceso a memoria remota entre nodos NUMA; la fabric añade latencia y aumenta la varianza.
Solución: Fija hilos calientes a un nodo NUMA, enlaza la memoria localmente (políticas systemd CPU/NUMA o numactl) y reduce migraciones (ajusta/deshabilita el balanceo NUMA automático para esa clase).
2) Symptom: “Upgraded RAM speed” and performance got worse
Causa raíz: Desacoplamiento o cambio de ratio FCLK/UCLK/MCLK aumentó la latencia efectiva de memoria; el ancho de banda pudo mejorar pero la latencia de la ruta crítica empeoró.
Solución: Prefiere perfiles de memoria de baja latencia para cargas sensibles. Valida con métricas p99 reales, no solo con pruebas sintéticas de ancho de banda.
3) Symptom: NVMe array benchmarks fine, production I/O latency is spiky
Causa raíz: IRQs manejadas en CPUs no locales, o hilos de almacenamiento programados lejos del root complex PCIe; el salto por la fabric añade latencia y fallas de caché.
Solución: Alinea la afinidad de IRQ y los hilos trabajadores con la localidad del dispositivo; considera colas por nodo NUMA. Vuelve a probar con concurrencia realista.
4) Symptom: VM performance inconsistent across hosts “with same CPU”
Causa raíz: Diferente particionado NUMA en BIOS (ajustes NPS), interleaving de memoria o cambios de firmware que afectan la topología y comportamiento de la fabric.
Solución: Estandariza perfiles de BIOS y firmware; captura la topología con lscpu/lstopo en el aprovisionamiento; aplica tamaño y pinning NUMA-aware para VMs.
5) Symptom: CPU utilization low, but the service is slow
Causa raíz: Stalls por latencia de memoria (memoria remota, fallos de caché), contención de locks amplificada por scheduling inter-nodos, o overhead de interrupciones I/O en cores equivocados.
Solución: Usa perf stat y estadísticas NUMA para confirmar stalls; ajusta colocación; reduce compartición entre nodos; corrige la localidad de IRQs.
6) Symptom: “It’s only one socket, so NUMA can’t matter”
Causa raíz: Topologías por chiplet crean comportamiento tipo NUMA dentro de un socket; Linux lo expone como nodos NUMA por una razón.
Solución: Trata sistemas multi-CCD de socket único como sistemas NUMA. Pon la localidad en tus runbooks y planificación de capacidad.
Tres microhistorias corporativas desde el terreno
Mini-story 1: The incident caused by a wrong assumption
Tenían un gateway de almacenamiento “simple”: algunos servidores EPYC, red rápida, cache NVMe y un stack I/O en espacio de usuario. En staging volaba. En producción tenía jitter—la latencia en cola subía en picos durante tráfico pico, y el on-call miraba discos y gráficos de red limpios como si Grafana los estuviera engañando.
La suposición equivocada era sutil: “socket único = memoria uniforme”. El equipo trató la máquina como un pool plano de cores y RAM. Dejaron que el kernel programara libremente y dejaron flotar los hilos de I/O porque “Linux hará lo correcto”. Linux hizo algo razonable. La carga necesitaba algo específico.
Los dispositivos PCIe NVMe eran locales a un nodo NUMA, pero los hilos de completación más ocupados a menudo corrían en otro. Cada completación I/O incluía un salto por la fabric, más fallos de caché porque las estructuras de datos más calientes vivían en otra región L3. A baja carga no importaba. A alta concurrencia, importó mucho.
Lo arreglaron con tres cambios: (1) fijar hilos de I/O a CPUs locales a los controladores NVMe, (2) enlazar la asignación de memoria de esos hilos al mismo nodo, (3) establecer afinidad de IRQ para evitar completaciones en CPUs aleatorios. La latencia se estabilizó, el throughput aumentó y el on-call dejó de ver problemas fantasma que solo aparecían después del almuerzo.
Mini-story 2: The optimization that backfired
Un equipo de plataforma quiso “rendimiento gratis” en una flota de servidores de aplicación. El plan: aumentar la frecuencia de memoria en BIOS. La herramienta del proveedor mostró operación estable, el burn-in pasó y las gráficas de un benchmark sintético se veían bien. Lo desplegaron en oleadas porque no eran imprudentes, solo optimistas.
Entonces el incidente: la latencia p99 de la API subió, no bajó. No en todos los servicios. Solo en ciertos servicios que usaban muchas estructuras pequeñas en memoria y muchos handoffs entre hilos. La utilización CPU bajó ligeramente (lo cual parecía “bueno” si no sabías mejor), pero los tiempos de respuesta empeoraron.
El postmortem fue incómodo: los nuevos ajustes de memoria empujaron el sistema a una relación de relojes diferente que aumentó la latencia efectiva de acceso a memoria. El ancho de banda mejoró. La latencia empeoró. Las cargas eran latencia-dependientes y muy sensibles a patrones de memoria remota, así que el retraso relacionado con la fabric se volvió visible en métricas de cola.
Revirtieron la “mejora” de memoria y rehicieron pruebas con dos perfiles: uno afinado para jobs por lotes intensivos en ancho de banda, otro para servicios de baja latencia. Estandarizaron perfiles por rol. La optimización dejó de ser “hacer el número más grande” y pasó a ser lo que siempre debió ser: ingeniería específica por carga.
Mini-story 3: The boring but correct practice that saved the day
Una compañía financiera corría un servicio sensible a latencia en EPYC. No hacían nada exótico. Lo que sí tenían era disciplina: cada rol de servidor tenía una configuración BIOS base, una configuración kernel base y un pequeño “informe de topología” capturado durante el aprovisionamiento y guardado con el registro del activo.
Una mañana tras un mantenimiento rutinario, un subconjunto de nodos mostró aumento de latencia en cola. No catastrófico, pero suficiente para disparar alertas. El on-call comparó dos informes de topología: el layout NUMA había cambiado. Mismo número de modelo, misma cantidad de RAM, pero un ajuste de BIOS había cambiado la partición NUMA. El comportamiento del scheduler cambió, la localidad de memoria cambió y la fabric empezó a hacer trabajo extra.
Revirtieron el perfil de BIOS al baseline conocido, rebootearon los nodos afectados y el problema desapareció. Sin heroísmos. Sin “deep dive” al kernel. Solo control de configuración y observabilidad.
Broma #2: La mejor solución de rendimiento a veces es una hoja de cálculo. No se lo digas a tus desarrolladores, pedirán tablas dinámicas.
Listas de verificación / plan paso a paso
Checklist: Before you tune anything
- Captura la salida de
lscpu,numactl --hardwareylstopo-no-graphicspara la clase de nodo. - Confirma que las versiones de BIOS/firmware sean consistentes en la flota para nodos comparables.
- Define métricas de éxito: latencia p50/p95/p99, throughput, tasa de errores, coste CPU por petición y (para storage) IOPS a latencias dadas.
- Ejecuta una prueba representativa de la carga. Los microbenchmarks sintéticos sirven para pistas; no son tu prueba de aceptación.
Step-by-step: Make a service NUMA-sane (lowest-risk path)
- Elige un objetivo de nodo NUMA basado en CPUs/memoria disponibles y localidad de dispositivos (NVMe/NIC).
- Fija los hilos del servicio a CPUs en ese nodo (systemd
CPUAffinity=o un wrapper). - Enlaza la asignación de memoria a ese nodo (
numactl --membindo políticas NUMA de systemd si las usas). - Corrige la localidad de IRQ para que las interrupciones del dispositivo aterricen en CPUs locales.
- Vuelve a probar bajo carga, compara latencia en cola y ciclos CPU por petición.
- Solo entonces toca relojes de memoria, boost o perillas de estado de energía—porque pueden mejorar medias mientras arruinan colas.
Step-by-step: Diagnose a “fabric-ish” performance regression after a change
- Confirma qué cambió: perfil BIOS, firmware, versión de kernel, microcode, ajustes de memoria, reglas de colocación de VM.
- Compara salidas de topología (nodos NUMA, mapeo de CPU) pre/post cambio.
- Revisa migraciones y spread de scheduling (
pidstat -t, migraciones en/proc/vmstat,numastat -p). - Comprueba el governor de CPU y la política p-state; restaura la política previa si es necesario.
- Revisa la distribución de IRQ y el nodo NUMA del dispositivo; restaura afinidad si se desplazó.
- Si debes revertir, hazlo rápido. Si debes mantener el cambio, implementa controles de localidad para compensar.
Preguntas frecuentes (FAQ)
1) Is Infinity Fabric “the same thing” as NUMA?
No. NUMA es un modelo visible por el software: el tiempo de acceso a memoria depende de qué nodo posee la memoria. Infinity Fabric es la interconexión hardware que a menudo hace reales esas diferencias en sistemas AMD con chiplets.
2) Why do I see multiple NUMA nodes on a single-socket EPYC server?
Porque el socket contiene múltiples chiplets y dominios de localidad. El kernel expone esto para que el scheduler y el asignador de memoria puedan tomar mejores decisiones. Ignorarlo está permitido, pero no es gratuito.
3) Should I disable kernel automatic NUMA balancing?
A veces. Para cargas genéricas y mixtas puede ayudar. Para servicios fijados y sensibles a latencia, puede introducir overhead de migración de páginas y jitter. Prueba ambas formas en un host canario con carga similar a producción.
4) Does faster DDR always improve performance on Ryzen/EPYC?
No. DDR más rápida puede mejorar ancho de banda, pero si cambia ratios de reloj o aumenta la latencia efectiva, algunas cargas empeoran—especialmente las sensibles a latencia en cola y caminos de fallo de caché.
5) How do I know if remote memory is hurting me?
Busca un desajuste: hilos ejecutándose en un nodo mientras las asignaciones de memoria viven en otros (pidstat + numastat -p). También vigila migraciones de páginas elevadas y IPC bajo con muchas fallas en LLC.
6) Can virtualization hide Infinity Fabric effects?
Puedes ocultar la causa y amplificar el dolor. Si una VM abarca nodos NUMA, puede incurrir frecuentemente en accesos a memoria remota. Exposición vNUMA adecuada, pinning y dimensionado importan en EPYC.
7) Is this only relevant for CPU-bound workloads?
Suele ser más relevante para cargas I/O y mixtas porque la localidad de interrupciones y los patrones de acceso a memoria dominan el comportamiento en cola. Tiempo de CPU “idle” no significa “rápido”.
8) What’s the single most effective operational control?
Colocación consciente de la topología: mantén hilos calientes, su memoria y las interrupciones de sus dispositivos en el mismo dominio de localidad. Reduce latencia, jitter y CPU desperdiciada.
9) Should I always pin everything?
No. El sobre-pinning puede causar carga desigual, inanición y mala utilización. Fija lo que es sensible a latencia o que es propietario de paths de I/O. Deja jobs por lotes y trabajo en background más flexibles.
10) What’s a good reliability mindset for this kind of tuning?
Usa experimentos controlados, canarios y perfiles por rol. Si no puedes revertir rápido, no estás “afinando”, estás apostando.
Próximos pasos que puedes hacer esta semana
Aquí está la ruta práctica que no destrozará tu flota:
- Inventaria la topología entre tipos de nodo: almacena salidas de
lscpu/numactl/lstopocon el registro del activo. - Elige un servicio sensible a latencia y hazlo NUMA-local (CPU + memoria + localidad de IRQ). Mide p99 y coste CPU.
- Estandariza un perfil BIOS y kernel por rol (latencia vs rendimiento vs lotes). “Un perfil para todos” es cómo te llevas sorpresas.
- Añade dos dashboards: (a) indicadores de memoria remota/migración (derivados de vmstat/numa), (b) distribución de IRQ por CPU y consumidores principales.
- Escribe un runbook con los pasos rápidos de diagnóstico arriba. El objetivo no es adorar a Infinity Fabric; es dejar de sorprenderte por la topología.
Una cita que vale la pena colgar en la pared, porque encaja con todo el tema: “La esperanza no es una estrategia.” — General Gordon R. Sullivan