Pruebas de CPU en el mundo real: un método sencillo para tu propia carga de trabajo

¿Te fue útil?

Cualquier reseña de CPU parece concluyente hasta que intentas servir tráfico real. Tu servicio no es “Cinebench”. Es un cóctel desordenado: syscalls, TLS, JSON, pausas de GC, interrupciones, fallos de caché y una llamada a la base de datos que a veces despierta en el lado equivocado de NUMA.

Si alguna vez desplegaste una actualización de CPU “segura” y aun así te llamaron por la latencia p99, ya conoces el problema: no mediste lo correcto. Este es un método pragmático para probar CPUs con tu carga de trabajo, con ejecuciones repetibles y decisiones que puedes defender en un postmortem.

Principios: qué significa “rendimiento de CPU” en producción

Las pruebas de CPU se descarrilan cuando la gente hace la pregunta equivocada. La pregunta equivocada es: “¿Qué CPU es más rápida?” La pregunta correcta es: “¿Qué CPU ofrece a mi carga de trabajo la latencia y el rendimiento que necesito, con coste y riesgo operativos aceptables?”

El rendimiento de CPU en producción es un problema de tres cuerpos:

  • Trabajo realizado (rendimiento): peticiones/seg, trabajos/min, filas escaneadas/seg.
  • Tiempo para hacerlo (latencia): especialmente la latencia de cola (p95/p99/p999).
  • Estabilidad: varianza bajo ruido—tareas en segundo plano, jitter, limitaciones, vecinos ruidosos.

Y “CPU” a menudo no es solo “CPU”. Es:

  • Microarquitectura: IPC, cachés, predictor de ramas, prefetchers.
  • Subsistema de memoria: ancho de banda, latencia, NUMA, fallos de página.
  • Comportamiento del kernel: planificación, cambios de contexto, interrupciones, cgroups.
  • Térmicas y límites de energía: comportamiento turbo que luce bien 30 segundos y luego se rinde.
  • Software: flags del compilador, librerías de criptografía, configuración de GC, locks.

El objetivo de las pruebas no es encontrar un número único. Es encontrar la envolvente de operación: bajo qué carga salta el p99, cuándo se forman colas de ejecución, cuándo empiezas a destrozar cachés y qué ajuste realmente mueve la aguja.

Idea parafraseada (John Allspaw, operaciones/fiabilidad): “No puedes reclamar fiabilidad a menos que puedas demostrarla en condiciones realistas.” Eso aplica también al rendimiento.

Una regla de la que seré molesto en insistir: prioriza la latencia primero, luego el coste. Las pruebas centradas sólo en throughput llevan a malas decisiones de CPU porque ocultan el dolor que sienten los usuarios en la cola.

Broma #1: Hacer benchmarks en una máquina de laboratorio inactiva es como probar una alarma de incendios en el vacío—técnicamente impresionante, operativamente inútil.

Hechos interesantes y contexto histórico

Estos no son trivias por el gusto de la trivia. Explican por qué las pruebas modernas de CPU no pueden reducirse a una sola etiqueta “GHz”.

  1. Las “guerras MHz” acabaron por una razón. Alrededor de mediados de los 2000 la frecuencia dejó de escalar limpiamente por densidad de potencia y fugas; el rendimiento se movió a multicore y ganancias microarquitectónicas.
  2. La ejecución especulativa lo cambió todo. La ejecución fuera de orden y la especulación mejoraron el IPC, pero también crearon clases de seguridad (Spectre/Meltdown) que luego introdujeron mitigaciones que afectan ciertas cargas.
  3. Turbo no es una promesa. Las CPUs modernas aumentan dinámicamente según potencia/temperatura/límites de corriente; dos ejecuciones “idénticas” pueden diferir si la refrigeración o la BIOS difieren.
  4. NUMA es antiguo, pero sigue siendo un gran culpable. Los sistemas NUMA multiprocesador llevan décadas siendo comunes; el modo de fallo (latencia de memoria remota) aún sorprende a equipos que migran desde nodos más pequeños.
  5. Los contadores de perf de Linux no se hicieron para dashboards. Los contadores de hardware nacieron para diseñadores de chips y perfilado de bajo nivel; usarlos operativamente requiere interpretación cuidadosa.
  6. Hyper-threading (SMT) depende de la carga. Puede aumentar el throughput en esperas, pero empeorar la latencia de cola en servicios con muchos locks o sensibles a caché.
  7. La virtualización maduró, luego los cgroups lo complicaron de nuevo. La sobrecarga de VM disminuyó, pero las cuotas de CPU en contenedores introdujeron un nuevo modo de fallo: throttling que parece “latencia misteriosa”.
  8. Las páginas grandes no son una ganancia universal. Huge pages reducen fallos de TLB, pero aumentan fragmentación y pueden perjudicar cuando la memoria es dinámica o sobreasignada.
  9. “CPU más rápida” puede ralentizar el sistema. Si la CPU procesa peticiones más rápido, puedes trasladar los cuellos de botella a almacenamiento, locks o servicios aguas abajo—entonces tu p99 empeora.

El método simple: construye un arnés de carga en el que confiar

Aquí está el método que recomiendo cuando quieres comparar CPUs o validar un cambio de afinamiento. Es simple, no simplista. Ejecutarás la misma carga entre candidatos, recogerás un pequeño conjunto de métricas y tomarás una decisión basada en latencia de cola y señales de saturación.

1) Define la carga como un adulto

“Tráfico API” no es una carga. Una carga es una distribución:

  • Mezcla de peticiones (endpoints, tipos de consulta, ratios lectura/escritura)
  • Tamaños de payload (pequeño, típico, patológico)
  • Modelo de concurrencia (hilos, async, tamaños de pool de conexiones)
  • Tiempo de pensamiento / patrón de llegada (tipo Poisson vs ráfagas)
  • SLO que te importa (p99 bajo N RPS)

Elige 1–3 escenarios representativos. No construyas todo un universo. Pero tampoco elijas solo el camino feliz.

2) Haz el entorno aburrido a propósito

La repetibilidad es una característica. Tu entorno de prueba debe eliminar ruido variable donde sea posible:

  • Fija el gobernador de frecuencia de la CPU a performance para las pruebas (o al menos regístralo).
  • Desactiva tormentas de cron en segundo plano, actualizaciones de paquetes y escaneos antivirus oportunistas.
  • Asegura versiones idénticas del kernel, ajustes de BIOS, microcódigo y mitigaciones entre candidatos—o acepta conscientemente diferencias y regístralas.
  • No compares un host bare-metal con una VM sobreasignada y lo llames “pruebas de CPU”. Eso es comedia.

3) Prueba para estado estable, no teatro de calentamiento

La mayoría de servicios tienen efectos de calentamiento: compilación JIT, cachés, pools de conexiones, caché de sistema de archivos, ARC, page cache, entrenamiento del predictor de ramas, incluso cachés DNS. Quieres dos fases:

  • Calentamiento: lo suficientemente largo para alcanzar comportamiento estable.
  • Medición: duración fija donde muestres métricas.

Cuando los equipos omiten esto, optimizan por “rendimiento del primer minuto” y luego se preguntan por qué a los 30 minutos se derrite. Tus CPUs no son calificadas por carisma.

4) Mide la distribución de latencia y las señales de saturación de CPU juntos

Si solo registras utilización de CPU, estás ciego. Si solo registras latencia, no sabes dónde mirar. Combínalos:

  • Latencia: p50/p95/p99, además del máximo (con cautela).
  • Rendimiento: RPS, QPS, jobs/sec.
  • Saturación de CPU: cola de ejecución, throttling, iowait (interpretado con cuidado).
  • Contadores perf (muestreo): cycles, instructions, branches, cache misses.

5) Compara en igualdad de “dolor de usuario”, no en igualdad de utilización

Al comparar dos CPUs, no iguales “CPU = 70%”. Igualen el punto SLO (por ejemplo, “p99 < 200ms”). Una CPU puede llegar al 70% navegando, otra al 50% ya estar thrashing por comportamiento de caché o límites de frecuencia.

6) Decide antes de ejecutar: qué resultado cambia tu decisión

Escribe tus criterios de aceptación:

  • “A 10k RPS, p99 < 150ms y tasa de errores < 0.1%.”
  • “Throttling de CPU < 1% bajo cuota.”
  • “Perf muestra IPC dentro del 10% del baseline; no hay pico anormal de cache misses.”

Si no decides antes, negociarás con las gráficas después del hecho. Y las gráficas siempre ganan.

Tareas prácticas (comandos, salida, decisiones)

A continuación hay tareas prácticas que puedes ejecutar en Linux. Cada una incluye: un comando, qué significa la salida y qué decisión tomar. Elige el subconjunto que coincida con tu entorno, pero no te saltes las que revelan “la CPU no es la CPU”.

Task 1: Recordar modelo de CPU, topología y estado SMT

cr0x@server:~$ lscpu
Architecture:           x86_64
CPU(s):                 32
Thread(s) per core:     2
Core(s) per socket:     16
Socket(s):              1
Model name:             Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz
NUMA node(s):           1
L3 cache:               48 MiB

Qué significa: Ahora sabes qué estás probando realmente: cores, sockets, SMT, tamaños de caché, NUMA. Las “32 CPUs” pueden ser 16 cores con SMT.

Decisión: Si comparas máquinas, asegura que la topología sea comparable, o incluye explícitamente diferencias de SMT/NUMA en tu conclusión. Si tu servicio es sensible a latencia, planea una ejecución A/B con SMT activado vs desactivado.

Task 2: Comprobar el gobernador de frecuencia de CPU y comportamiento actual

cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
ondemand

Qué significa: “ondemand” puede añadir jitter y sub-boost durante picos cortos, dependiendo del kernel y la plataforma. En servidores, a menudo quieres comportamiento consistente.

Decisión: Para benchmarking, pon el gobernador en performance (o regístralo y acepta variabilidad). Si no puedes cambiarlo (nube), al menos regístralo y ejecuta más tiempo para promediar el comportamiento de boost.

Task 3: Verificar capacidad de turbo/boost (cuando esté disponible)

cr0x@server:~$ cat /sys/devices/system/cpu/intel_pstate/no_turbo
0

Qué significa: 0 significa que turbo está permitido. Si es 1, tu “CPU rápida” podría comportarse como una CPU educada.

Decisión: Mantén turbo consistente entre hosts de prueba. Si un host tiene turbo desactivado (BIOS u OS), tu comparación ya está corrompida.

Task 4: Comprobar throttling de CPU debido a cgroups (contenedores/Kubernetes)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 987654321
user_usec 800000000
system_usec 187654321
nr_periods 12345
nr_throttled 2345
throttled_usec 456789012

Qué significa: nr_throttled y throttled_usec indican que el kernel está deteniendo forzosamente tu carga para imponer la cuota de CPU.

Decisión: Si el throttling es relevante durante tu prueba, tu “benchmark de CPU” es un benchmark de cuota. Aumenta la cuota, elimina límites para la prueba o trata el “throttling por petición” como una métrica primaria.

Task 5: Vigilar la cola de ejecución y uso de CPU a lo largo del tiempo

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
 2  0      0 8123456 123456 789012  0    0     0     2  900 1400 25  5 70  0  0
 8  0      0 8121200 123456 789100  0    0     0     0 1200 6000 80 10 10  0  0
10  0      0 8119000 123456 789200  0    0     0     0 1300 7000 85 10  5  0  0
 9  0      0 8118000 123456 789250  0    0     0     0 1250 6800 83 12  5  0  0
 3  0      0 8120000 123456 789300  0    0     0     0 1000 3000 50  7 43  0  0

Qué significa: r son procesos ejecutables. Si r excede consistentemente el número de cores, estás saturado o en contención de CPU. Picos en cs (cambios de contexto) pueden apuntar a contención de locks o sobreasignación.

Decisión: Si la cola de ejecución crece mientras la latencia se dispara, probablemente necesitas más CPU (o menos hilos) o estás golpeando un lock. Si la cola está baja pero la latencia alta, el cuello de botella está en otro lado.

Task 6: Inspeccionar utilización por core y tiempo robado (steal) en virtualización

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.1.0 (server) 	01/12/2026 	_x86_64_	(32 CPU)

12:00:01 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:02 AM  all   62.00 0.00 8.00 0.10    0.00 1.50  5.00   23.40
12:00:02 AM   0    90.00 0.00 5.00 0.00    0.00 0.00  2.00    3.00
12:00:02 AM   1    10.00 0.00 5.00 0.00    0.00 0.00 20.00   65.00
12:00:03 AM  all   64.00 0.00 7.50 0.00    0.00 1.20  6.50   20.80

Qué significa: %steal muestra al hipervisor tomando tiempo de CPU. Un steal alto hace la latencia ruidosa e invalida comparaciones entre ejecuciones.

Decisión: Si steal > 1–2% durante las pruebas, muévete a hosts/instancias dedicadas o trata el entorno como “no apto para comparación de CPU”.

Task 7: Comprobar el layout NUMA y si tu proceso está rebotando memoria

cr0x@server:~$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 64250 MB
node 0 free: 12000 MB
node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 64250 MB
node 1 free: 15000 MB
node distances:
node   0   1
  0:  10  21
  1:  21  10

Qué significa: El acceso remoto a memoria (distancia 21 vs 10) es significativamente más lento. Si tu proceso está programado en el nodo 0 pero accede frecuentemente a memoria del nodo 1, tu CPU parece “lenta”.

Decisión: Para pruebas, fija la localidad de CPU y memoria para la carga (o prueba explícitamente con y sin fijación). Si migras a dual-socket, trata NUMA como un requisito de primera clase.

Task 8: Detectar migraciones de CPU (un asesino común de latencia)

cr0x@server:~$ perf stat -e task-clock,context-switches,cpu-migrations,page-faults -p 1234 -- sleep 10
 Performance counter stats for process id '1234':

    10012.34 msec task-clock                #    0.999 CPUs utilized
        120,345      context-switches       #   12.014 K/sec
          3,210      cpu-migrations         #  320.593 /sec
          8,765      page-faults            #  875.539 /sec

      10.012345678 seconds time elapsed

Qué significa: Miles de migraciones de CPU por segundo pueden destruir la localidad de caché y causar jitter. Los cambios de contexto pueden indicar sobreasignación de hilos, locks o IO bloqueante.

Decisión: Si las migraciones son altas, considera fijar CPUs, reducir hilos ejecutables o arreglar la presión del scheduler (por ejemplo, colocación de cgroups). Si los cambios de contexto son altos y la latencia es irregular, busca contención de locks o pools de hilos excesivos.

Task 9: Medir IPC y comportamiento de caché con perf (vista macro)

cr0x@server:~$ perf stat -e cycles,instructions,branches,branch-misses,cache-references,cache-misses -a -- sleep 10
 Performance counter stats for 'system wide':

  32,100,000,000      cycles
  45,500,000,000      instructions              #    1.42  insn per cycle
   8,900,000,000      branches
     120,000,000      branch-misses             #    1.35% of all branches
   2,100,000,000      cache-references
     210,000,000      cache-misses              #   10.00% of all cache refs

      10.000987654 seconds time elapsed

Qué significa: El IPC y las tasas de cache-miss cambian dramáticamente entre CPUs y cargas. Una CPU con relojes más altos puede perder frente a otra con mejor comportamiento de caché en servicios reales.

Decisión: Si el IPC colapsa o los cache misses se disparan bajo carga, céntrate en localidad de memoria, estructuras de datos y modelo de concurrencia—no solo en “comprar núcleos más rápidos”. Usa esto para explicar por qué una CPU sintética “peor” puede ganar para tu servicio.

Task 10: Confirmar si realmente estás limitado por CPU o por memoria

cr0x@server:~$ perf stat -e cycles,instructions,stalled-cycles-frontend,stalled-cycles-backend -p 1234 -- sleep 10
 Performance counter stats for process id '1234':

  5,400,000,000      cycles
  6,900,000,000      instructions              #    1.28  insn per cycle
  1,900,000,000      stalled-cycles-frontend
  2,700,000,000      stalled-cycles-backend

      10.001234567 seconds time elapsed

Qué significa: Las largas esperas en el backend suelen correlacionar con latencia/ancho de banda de memoria, fallos de caché o límites de recursos de la pipeline.

Decisión: Si las esperas del backend dominan, una CPU con mejor subsistema de memoria (caches más grandes, más canales) puede superar a una CPU de mayor frecuencia. También considera NUMA y velocidad de memoria antes de reescribir medio código.

Task 11: Identificar principales consumidores de CPU y si son usuario vs kernel

cr0x@server:~$ top -b -n 1 | head -n 15
top - 00:00:10 up 12 days,  3:12,  2 users,  load average: 12.50, 11.20, 9.80
Tasks: 310 total,   8 running, 302 sleeping,   0 stopped,   0 zombie
%Cpu(s): 82.0 us, 10.0 sy,  0.0 ni,  8.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem : 128000.0 total,  22000.0 free,  53000.0 used,  53000.0 buff/cache

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1234 app       20   0 4567890 512000  25000 R  750.0   0.4  10:12.3 myservice
 2345 root      20   0       0      0      0 S   80.0   0.0   1:20.1 ksoftirqd/3

Qué significa: Un alto sy (sistema) junto con ksoftirqd sugiere que manejo de red/interrupciones está consumiendo CPU. Eso sigue siendo CPU, pero la solución no es “optimizar el parsing JSON”.

Decisión: Si la CPU del kernel es alta, inspecciona interrupciones, ajustes de NIC, tasas de paquetes, conntrack y opciones de offload TLS. Si la CPU de usuario es alta, perfila la aplicación.

Task 12: Comprobar distribución de interrupciones (común en altas tasas de paquetes)

cr0x@server:~$ cat /proc/interrupts | head -n 8
           CPU0       CPU1       CPU2       CPU3
  24:    1234567     1200345       98012       87000  IR-PCI-MSI  eth0-TxRx-0
  25:      98012       87000    1123456     1198765  IR-PCI-MSI  eth0-TxRx-1
NMI:       1234       1234       1234       1234  Non-maskable interrupts
LOC:   98765432   98765000   98764900   98764800  Local timer interrupts

Qué significa: Si una CPU está abrumada por interrupciones de NIC, verás hotspots y latencia en cola. Idealmente, las interrupciones se balancean entre cores (o se fijan intencionalmente).

Decisión: Si las interrupciones están sesgadas, activa/verifica irqbalance, ajusta colas RSS o fija interrupciones lejos de hilos críticos para latencia.

Task 13: Medir presión del scheduler (PSI) para confirmar contención

cr0x@server:~$ cat /proc/pressure/cpu
some avg10=0.50 avg60=0.80 avg300=0.75 total=123456789
full avg10=0.10 avg60=0.25 avg300=0.20 total=23456789

Qué significa: PSI te dice el tiempo pasado esperando CPU. “full” indica tareas que no pueden ejecutarse en absoluto por falta de CPU. Esto es extremadamente accionable para “¿realmente es contención de CPU?”

Decisión: Si PSI “full” sube durante picos de latencia, estás falto de CPU o throttled. Añade CPU, reduce concurrencia o elimina quotas. Si PSI es bajo, deja de culpar a la CPU y busca en otro lado.

Task 14: Confirmar que disco y sistema de archivos no están ensuciando tu prueba de CPU

cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 	01/12/2026 	_x86_64_	(32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          70.00    0.00   10.00    0.20    0.00   19.80

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await
nvme0n1         10.00   20.00  800.00  900.00    0.00    1.00   5.00   0.40

Qué significa: Bajo %util y bajo await sugieren que el almacenamiento no es el cuello de botella. Un iowait alto no siempre es “disco lento”, pero esto te da una línea base.

Decisión: Si la utilización de disco y await se disparan durante las “pruebas de CPU”, estás midiendo IO o comportamiento de fsync. Aisla IO, mueve a tmpfs para pruebas puramente CPU o acepta IO como parte de la carga (a menudo correcto para bases de datos).

Task 15: Ejecutar una prueba de carga controlada y capturar percentiles de latencia

cr0x@server:~$ hey -z 60s -c 200 -q 50 http://127.0.0.1:8080/api/v1/search
Summary:
  Total:	60.0012 secs
  Slowest:	0.4123 secs
  Fastest:	0.0102 secs
  Average:	0.0451 secs
  Requests/sec:	9700.12

Response time histogram:
  0.010 [1]	|
  0.050 [520000]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.100 [55000]	|■■■
  0.200 [4000]	|
  0.400 [120]	|

Latency distribution:
  50% in 0.0400 secs
  90% in 0.0800 secs
  99% in 0.1500 secs

Qué significa: Esto te da una vista externa: lo que experimenta un cliente. El p99 es tu “medidor de dolor del cliente”.

Decisión: Compara CPUs con la misma carga ofrecida y observa p99/p999. Si la CPU A te da 20% más RPS pero duplica p99, la CPU A es una trampa para servicios sensibles a latencia.

Task 16: Perfilar hotspots sin cambiar código (entrada rápida para flamegraph)

cr0x@server:~$ perf record -F 99 -p 1234 -g -- sleep 30
[ perf record: Woken up 8 times to write data ]
[ perf record: Captured and wrote 12.345 MB perf.data (12345 samples) ]

Qué significa: Has recogido pilas de llamadas. Ahora puedes identificar si el tiempo se gasta en criptografía, parsing JSON, asignación de memoria o syscalls del kernel.

Decisión: Si los hotspots están en networking del kernel, afina kernel/NIC. Si están en asignación/GC, ajusta el runtime o reduce asignaciones. Si están en un lock, arregla la concurrencia. Probar CPUs sin perfilar es conjetura con gráficas más bonitas.

Guion de diagnóstico rápido

Esta es la versión “estoy de guardia y la gráfica está ardiendo”. Quieres decidir rápido si estás limitado por CPU, throttleado o sufriendo otro cuello de botella con mascarilla de CPU.

Primero: confirma el dolor del usuario y si se correlaciona con la presión de CPU

  • Revisa latencia p95/p99 (desde la salida de la prueba de carga o métricas del servicio). Si la latencia de cola está estable, no persigas CPU solo porque la utilización parezca alta.
  • Revisa PSI de CPU (/proc/pressure/cpu). Si “full” sube durante picos de latencia, la contención/throttling de CPU es real.
  • Revisa la cola de ejecución (vmstat 1, uptime). Cola sostenida por encima del número de cores es una señal clásica de saturación.

Segundo: descarta a los dos impostores principales de la CPU

  • Throttling: cuotas de contenedor (/sys/fs/cgroup/cpu.stat) y steal en la nube (mpstat).
  • NUMA/migraciones: si eres multi-socket o tienes SLOs estrictos, revisa numactl --hardware y perf stat ... cpu-migrations.

Tercero: decide si necesitas más CPU, otra CPU o código distinto

  • Más CPU cuando cola de ejecución + PSI son altos y el perfil muestra cómputo real.
  • Otra CPU cuando perf muestra stalls de memoria, cache misses o colapso de frecuencia. Puede que necesites cachés más grandes, más canales de memoria o turbo sostenido, no solo más cores.
  • Código distinto cuando los hotspots son obvios y arreglables (contención de locks, tormenta de asignaciones, syscalls chatty).

Broma #2: Si tu p99 mejora solo cuando dejas de medirla, has inventado rendimiento por observación—por favor no metas eso en producción.

Tres micro-historias corporativas desde las trincheras

Micro-historia #1: El incidente causado por una suposición equivocada (SMT “siempre es rendimiento gratis”)

Una compañía ejecutaba una API sensible a latencia que hacía autenticación, validación JSON y una pequeña consulta a la base de datos. Migraron a una generación de CPU más reciente y vieron grandes números sintéticos. Alguien notó que podían activar SMT en todo y “obtener 30% más CPU” sin comprar nada. El despliegue empezó con confianza y una hoja de cálculo celebratoria.

En pocas horas, el p99 de las peticiones de login empezó a tambalearse. No dramáticamente al principio—lo suficiente para romper sus alertas internas de SLO. La tasa de errores se mantuvo baja, la latencia media parecía bien y la utilización de CPU en realidad bajó ligeramente porque las peticiones “completaban”. El ingeniero de guardia tuvo el peor tipo de gráfica: la que no grita, solo decepciona.

Perseguieron a la base de datos. Ajustaron pools de conexión. Incluso culparon al balanceador. El verdadero culpable fue embarazosa y físicamente real: SMT incrementó la contención en recursos de ejecución compartidos y cachés para una ruta de autenticación con muchos locks y ramas. Bajo tráfico en ráfaga, los hilos extra aumentaron los cambios de contexto y empeoraron la latencia de cola.

La solución no fue ideológica (“SMT malo”). Hicieron pruebas controladas con SMT on/off y descubrieron una división: los endpoints batch querían SMT, login no. Terminaron desactivando SMT para la capa de latencia y manteniéndolo para la capa batch. El incidente no fue “SMT rompió producción”. Fue una suposición equivocada: que las características de CPU son universalmente beneficiosas.

Qué aprender: trata SMT como cualquier otra variable. Mide bajo tu concurrencia real y vigila latencia de cola y migraciones. Los benchmarks solo de throughput te mentirán con cara seria.

Micro-historia #2: La optimización que salió mal (la mejora de CPU cambió el cuello de botella y empeoró p99)

Otra organización tenía un servicio marginalmente CPU-bound: mucho termination TLS y algo de compresión de respuesta. Mejoraron a una CPU de mayor frecuencia con mejor aceleración criptográfica. El RPS antes/después mejoró. Las gráficas en la revisión de la actualización parecían limpias. Todos se relajaron.

Luego, una semana después, el p99 subió durante los picos diarios normales. No siempre. No de forma predecible. Las CPUs nuevas eran “más rápidas”, pero las quejas de clientes aumentaron. El equipo reaccionó como suelen hacerlo: añadir más pods, más nodos, subir límites de autoscaling. Ayudó, pero el coste se disparó y el problema no desapareció del todo.

El problema real: al hacer el front-end más rápido, aumentaron la tasa de peticiones hacia caches aguas abajo y una tienda clave compartida. Esa tienda tenía una rutina de compactación pesada en CPU que antes era invisible porque el front-end no podía generar suficiente presión. Ahora sí podía. El cuello de botella se movió, y la latencia de cola siguió al eslabón más débil.

Lo arreglaron limitando la concurrencia hacia la tienda aguas abajo, añadiendo retropresión y ajustando TTLs de cache para reducir patrones de estampida. La mejora de CPU no estuvo mal; la suposición de que la CPU es una propiedad local fue la errada.

Qué aprender: las mejoras de CPU cambian la dinámica del sistema. Cuando pruebes CPUs, incluye el comportamiento de dependencias aguas abajo o aíslalas explícitamente. Si pruebas solo un componente en aislamiento, estás haciendo una demo, no planificación de capacidad.

Micro-historia #3: La práctica aburrida pero correcta que salvó el día (arnés repetible + entorno fijado)

Un equipo de almacenamiento se preparaba para comprar nuevos nodos para una plataforma analítica interna. Tenían dos opciones de CPU: una con más cores, otra con menos pero cores más rápidos y caché mayor. Las opiniones eran intensas, como siempre pasa con hardware.

En lugar de discutir, construyeron un arnés aburrido y disciplinado. Misma imagen de OS, mismo kernel, mismos ajustes de BIOS, misma base de microcódigo. Fijaron el gobernador de CPU, desactivaron servicios innecesarios y ejecutaron una fase de calentamiento antes de medir. Capturaron p50/p95/p99 para latencia de consultas, además de contadores perf y PSI. Cada ejecución fue etiquetada con la revisión git exacta y un timestamp. Nadie pudo “probar un ajuste más” sin registrarlo.

Encontraron algo contraintuitivo: la CPU “más cores” ganó en throughput bruto, pero la CPU con “caché mayor” ganó en p99 para consultas con joins pesados. Los contadores perf mostraron tasas mayores de cache-miss y stalls de backend en la opción many-core. La conclusión no fue filosófica; fue datos: si querían rendimiento interactivo estable, la CPU con caché mayor era la apuesta más segura.

Cuando procurement preguntó por qué elegían el SKU “menos denso en cores”, tenían un resumen de una página con gráficos y comandos reproducibles. Sin heroísmos. Solo competencia aburrida. Evitó que una discusión de millones terminara en un error de millones.

Qué aprender: la herramienta de rendimiento más valiosa es un arnés repetible. No es glamuroso. Es cómo evitas comprar arrepentimiento.

Errores comunes: síntomas → causa raíz → solución

Esta sección sirve para diagnosticar modos de fallo que aparecen repetidamente en pruebas reales de CPU. El patrón importa: el síntoma es lo que ves; la causa raíz es lo que realmente pasa; la solución es lo que debes hacer a continuación.

1) Síntoma: la CPU está “baja”, pero la latencia p99 es alta

  • Causa raíz: Esperas de IO, contención de locks o latencia de dependencias aguas abajo. La utilización promedio de CPU oculta stalls.
  • Solución: Revisa iostat -xz, verifica PSI de memoria/IO (si está disponible) y perfila para locks. Añade tiempos de dependencia a la salida de tu prueba.

2) Síntoma: CPU está a tope, pero el throughput no aumenta con más clientes

  • Causa raíz: Lock global, punto de serialización o hilo caliente único (por ejemplo, GC, event loop, logging).
  • Solución: top -H para encontrar hilos calientes; usa perf record; reduce contención; re-arquitecta la sección crítica.

3) Síntoma: los resultados varían salvajemente entre ejecuciones

  • Causa raíz: Escalado de frecuencia/turbo diferente, throttling térmico, tiempo steal, ruido en segundo plano o efectos de calentamiento.
  • Solución: Fija el gobernador, monitorea frecuencia/temperaturas, asegura no tener steal, ejecuta más tiempo con calentamiento y registra detalles del entorno.

4) Síntoma: la “mejor CPU” gana en latencia media pero pierde en p99

  • Causa raíz: Jitter de scheduling, contención por SMT, migraciones o thrash de caché bajo ráfaga.
  • Solución: Revisa migraciones, cola de ejecución y contadores perf; prueba con SMT desactivado; fija cores para hilos críticos de latencia.

5) Síntoma: aparece throttling de CPU solo en contenedores

  • Causa raíz: Cuota de cgroup demasiado baja, periodo demasiado corto o carga bursty que golpea los bordes de la cuota.
  • Solución: Aumenta límites, considera usar requests/limits de CPU cuidadosamente, o evita cuotas estrictas para servicios críticos de latencia.

6) Síntoma: alto CPU de sistema y picos de ksoftirqd

  • Causa raíz: Presión de interrupciones/softirq: alta tasa de paquetes, paquetes pequeños, conntrack o colas de NIC subóptimas.
  • Solución: Balancea interrupciones, afina RSS/colas, revisa ajustes de conntrack y considera offloads con cuidado (¡mide!).

7) Síntoma: la CPU parece saturada solo en sistemas dual-socket

  • Causa raíz: Memoria remota NUMA o locking entre nodos; asignaciones de memoria no locales a los cores que ejecutan.
  • Solución: Fija procesos con numactl, usa allocators conscientes de NUMA, alinea IRQs y hilos, y prueba escalado por socket.

8) Síntoma: “optimizas” aumentando hilos y el rendimiento empeora

  • Causa raíz: Overhead de cambios de contexto, contención de locks, thrash de caché. Más hilos puede reducir la eficiencia de CPU.
  • Solución: Reduce concurrencia; usa async cuando convenga; dimensiona pools de hilos según cores; mide cambios de contexto y migraciones.

Listas de verificación / plan paso a paso

Checklist A: Un plan sensato de comparación de CPU (bare metal o VMs dedicadas)

  1. Elige escenarios: 1–3 mezclas de carga que representen producción (incluye un escenario “mal día”).
  2. Congela el entorno: mismo OS, kernel, base de microcódigo, misma configuración de servicio, mismas versiones de dependencias.
  3. Controla el comportamiento de CPU: registra governor/turbo; evita diferencias térmicas; asegura refrigeración y límites de potencia similares.
  4. Calentamiento: ejecuta una fase de calentamiento (5–15 minutos según cachés/JIT/DB).
  5. Fase de medición: duración fija (10–30 minutos) con carga ofrecida estable.
  6. Recoge métricas: distribución de latencia, rendimiento, tasa de errores, cola de ejecución, PSI, throttling de cgroup, snapshot de contadores perf.
  7. Repite: al menos 3 ejecuciones por escenario; descarta outliers obvios solo con una razón (pico de steal, evento de deploy).
  8. Compara al SLO: qué CPU alcanza tu objetivo p99 al menor coste y menor riesgo operativo?
  9. Documenta el arnés: comandos, configuraciones y un resumen de una página “qué cambió”.

Checklist B: Un plan rápido “¿es CPU o no?”

  1. Mira latencia p99 y tasa de errores primero. Si los usuarios no sufren, no te lances a tunear.
  2. Revisa PSI de CPU y cola de ejecución. Si ambos son bajos, la CPU no es tu recurso limitante.
  3. Revisa throttling de cgroup y steal. Si están presentes, arregla el entorno antes de interpretar resultados.
  4. Revisa CPU del sistema e interrupciones. Si el tiempo de kernel es alto, estás peleando con el camino kernel/NIC.
  5. Sólo entonces perfila la app. No perfiles a ciegas; trae evidencia.

Checklist C: Decidir qué comprar (o si afinar en su lugar)

  • Si tu carga es sensible a caché/memoria: prioriza caches más grandes, mejor ancho de banda de memoria y amigabilidad NUMA sobre el pico de boost por núcleo.
  • Si tu carga es embarrassingly parallel: los cores importan, pero vigila rendimientos decrecientes por locking y recursos compartidos.
  • Si tu carga es crítica para latencia: reduce fuentes de jitter (SMT, migraciones, throttling), prefiere rendimiento consistente en todos los cores y mide p99 bajo ráfaga.
  • Si estás dentro de contenedores: trata el throttling como una restricción de primera clase; una “CPU rápida” puede volverse lenta por política.

Preguntas frecuentes

1) ¿Debo usar benchmarks sintéticos en absoluto?

Úsalos como guardarraíles, no como decisores. Ayudan a detectar hosts rotos (mala refrigeración, BIOS errónea, turbo apagado). No reemplazan las pruebas con la carga real.

2) ¿Cuántas ejecuciones son “suficientes” para tener confianza?

Para entornos estables, tres ejecuciones por escenario es un mínimo razonable. Si la varianza es alta, arreglar la varianza es el trabajo real; más ejecuciones solo cuantifican el caos.

3) ¿Cuál es la métrica de CPU más útil además de utilización?

CPU Pressure Stall Information (PSI) es brutalmente práctica. Responde: “¿Las tareas esperan tiempo de CPU?” Se correlaciona bien con la latencia visible al usuario bajo saturación.

4) ¿Es iowait una señal fiable de que estoy limitado por IO?

Es una pista, no un veredicto. iowait puede ser bajo incluso cuando IO es tu cuello de botella (IO asíncrono, hilos bloqueados en otro lugar), y puede ser alto por artefactos de planificación. Combínalo con iostat -xz y tiempos de la aplicación.

5) ¿Debo desactivar SMT en producción?

A veces. Si tu carga es pesada en locks, sensible a caché o crítica en p99, SMT puede aumentar jitter. Si tu carga busca throughput y suele bloquear, SMT puede ayudar. Prueba ambas opciones; no adivines.

6) ¿Por qué mi benchmark de CPU luce bien 30 segundos y luego empeora?

Turbo y límites térmicos. Muchas CPUs aceleran agresivamente hasta alcanzar límites sostenidos de potencia/temperatura. Ejecuta lo suficientemente largo para alcanzar estado estable y monitorea frecuencia/temperaturas.

7) ¿Puedo hacer pruebas reales de CPU en instancias compartidas en la nube?

Puedes hacer pruebas de carga, pero la comparación de CPU es arriesgada. Steal, vecinos ruidosos y políticas de turbo variables introducen varianza. Si debes, usa instancias dedicadas o al menos registra %steal y ejecuta más tiempo.

8) ¿Qué pasa si perf está restringido en mi entorno?

Entonces confía más en percentiles de latencia externos, PSI, cola de ejecución y perfilado a nivel de aplicación. Los contadores perf son geniales, pero no obligatorios para tomar una decisión correcta.

9) ¿Cómo sé si estoy limitado por ancho de banda de memoria vs cómputo?

Busca IPC bajo y stalls de backend altos bajo carga, además de sensibilidad al pinning NUMA y velocidad de memoria. Si el rendimiento mejora dramáticamente al mejorar localidad, no es “solo CPU”.

10) ¿Cuál es un criterio de éxito realista para un cambio de CPU?

Defínelo como un aumento de capacidad a SLO fijo: “Con p99 < X ms, podemos manejar Y% más throughput.” Ese es el número con el que puedes operar el negocio.

Conclusión: siguientes pasos que realmente funcionan

Las pruebas de CPU en el mundo real no tratan de ganar discusiones. Tratan de comprar (o afinar) rendimiento que puedas mantener a las 3 a.m. cuando la caché está fría, el tráfico es ráfaga y el cron de alguien está haciendo algo “útil”.

Haz esto a continuación:

  1. Escribe un escenario basado en SLO (carga ofrecida + objetivo p99) y un escenario “mal día”.
  2. Construye un arnés repetible: calentamiento, medición, registra entorno, recoge latencia + PSI + cola de ejecución + throttling.
  3. Ejecuta tres veces en tu CPU actual y establece una baseline en la que confíes.
  4. Cambia una variable (modelo de CPU, SMT, cuota) y vuelve a ejecutar. Si cambias cinco cosas, no aprendes nada.
  5. Toma la decisión con igualdad de dolor del usuario: elige la CPU (o afinamiento) que cumpla p99 con el menor riesgo operativo.

Si solo recuerdas una cosa: mide la carga, no el hardware. El hardware es solo el escenario. Tu software es la obra. Y producción es el crítico que nunca duerme.

← Anterior
Apache para WordPress: módulos y reglas que rompen sitios (y cómo solucionarlo)
Siguiente →
La decisión del PC de IBM que creó la industria de clones

Deja un comentario