Thread Director: por qué la CPU ahora aconseja al sistema operativo

¿Te fue útil?

Nada simboliza la “computación moderna” como una estación de trabajo de 64 núcleos que se bloquea al abrir una videollamada, o un servidor de compilación que aleatoriamente tarda el doble los martes. Miras el load average: bien. Miras el uso de CPU: bien. Sin embargo, la latencia máxima se dispara, se escuchan chasquidos de audio o la base de datos decide que el percentil 99 es un estilo de vida.

Si estás ejecutando en CPUs híbridas (núcleos grandes + núcleos pequeños), un cambio silencioso está en el centro de muchas quejas de “antes era predecible”: la CPU ahora aconseja activamente al sistema operativo dónde deberían ejecutarse tus hilos. Esto es territorio de Thread Director—donde la planificación deja de ser solo un problema de política del SO y se convierte en una negociación CPU + SO.

Qué cambió: de la planificación del SO a la intención suministrada por la CPU

Durante décadas tratamos la planificación de la CPU como un problema del SO. El kernel veía hilos ejecutables, medía cuánto tiempo habían corrido, aplicaba heurísticas de equidad y los colocaba en CPUs según topología y política. El hardware mayormente se mantenía en su carril: ejecutaba instrucciones, generaba interrupciones y expone contadores.

Los diseños de CPU híbrida rompieron esa separación limpia. Cuando no todos los núcleos son iguales, “una CPU es una CPU” se convierte en una mentira que el planificador no puede permitirse. Aun puedes planificar de forma justa, pero lo harás de forma costosa: quemando energía en P-cores para trabajos de fondo, o privando de recursos a trabajo sensible a la latencia en E-cores. El resultado parece “variación aleatoria del rendimiento”, porque lo es.

Thread Director (nombre de Intel; el concepto existe más ampliamente) es la CPU admitiendo lo obvio: tiene mejor visibilidad en tiempo real de qué está haciendo un hilo en este momento. No “este hilo usó 70% CPU en 10 ms”, sino “este hilo es intensivo en instrucciones retiradas, con muchas faltas de caché, ramificado, vectorizado, bloqueado o despierta con frecuencia”. La CPU puede clasificar ese comportamiento continuamente y enviar orientación—pistas, no órdenes—al planificador del SO.

Ese es el cambio ideológico: la planificación deja de ser puramente un motor de políticas que opera con métricas gruesas. Es política más telemetría casi en tiempo real desde el silicio.

Una cita que vale la pena llevar a las revisiones de diseño, atribuida a Werner Vogels: “Everything fails, all the time.” Si no estás construyendo tus suposiciones de planificación alrededor de eso, las estás construyendo alrededor de la esperanza.

CPUs híbridas en términos sencillos (y por qué tu modelo mental está desactualizado)

Híbrido significa al menos dos clases de núcleos:

  • Performance cores (P-cores): mayor rendimiento por hilo, típicamente mayores techos de frecuencia, más máquina out-of-order y mayor consumo de energía.
  • Efficiency cores (E-cores): más pequeños, menor consumo, a menudo excelente rendimiento por vatio, pero con pico de rendimiento por hilo menor y a veces rasgos microarquitectónicos diferentes.

Híbrido no es nuevo en el mundo. Los SoC móviles llevan años con big.LITTLE. Lo novedoso es que la arquitectura híbrida aparece ampliamente en escritorios, portátiles e incluso en algunos flujos de trabajo cercanos a servidores donde la gente asume “puedo fijar y olvidar”. Esa asunción ahora es un ticket de incidente recurrente.

El planificador tiene que decidir: qué hilos merecen P-cores, cuáles pueden vivir en E-cores y cuándo moverlos. La migración tiene un coste: calor de caché, estado del predictor de saltos y a veces comportamiento de frecuencia. Pero dejar a un hilo “en el barrio equivocado” también tiene un coste: latencia, rendimiento o duración de batería.

Aquí es donde se vuelve picante para sistemas en producción:

  • La mayoría de las aplicaciones no declaran intención. No le dicen al SO “este es un hilo sensible a la latencia”, o “este es un trabajador en segundo plano”, o “este es un trabajo limitado por ancho de banda de memoria que no se beneficia de P-cores”.
  • Los planificadores inferen intención a partir del comportamiento. El comportamiento cambia rápidamente: un hilo trabajador HTTP puede estar inactivo, luego parsear TLS, luego tocar la base de datos, luego comprimir una respuesta.
  • El hardware ve el comportamiento antes y con mayor granularidad que el SO.

Thread Director existe porque al SO se le pedía tomar decisiones rápidas con gafas borrosas.

Broma corta #1: Fijar hilos en CPUs híbridas es como reservar una mesa en un restaurante sin comprobar qué sala tiene la cocina.

Qué hace realmente Thread Director

A grandes rasgos, Thread Director es un sistema de telemetría y clasificación en hardware que:

  1. Observa el comportamiento por hilo mediante contadores de rendimiento y señales microarquitectónicas.
  2. Clasifica los hilos en categorías relacionadas con “cuánto se benefician de P-cores” y “qué tan sensibles son a la latencia”.
  3. Comunica esas clasificaciones al planificador del SO usando una interfaz definida.
  4. Permite al SO colocar hilos en núcleos apropiados con mayor precisión que las heurísticas puras.

Lo que no es

  • No es un interruptor mágico para “hacer todo más rápido”. Si tu carga está limitada por memoria, el P-core quizá solo espere más rápido.
  • No sustituye la planificación de capacidad. Si saturas la máquina, seguirás saturándola—solo con decisiones más caras.
  • No es una excusa para dejar de medir. Es una razón para medir de forma distinta.

El impacto operativo real

El mayor impacto es la varianza. Un híbrido sin buena planificación produce varianza desagradable: latencia inconsistente, tiempos de compilación inconsistentes, tiempos de consulta inconsistentes. Thread Director busca reducir eso mejorando las decisiones de colocación ante el cambio de comportamiento de los hilos.

Pero también cambia dónde depuras. “El planificador del SO es tonto” se convierte en “el planificador del SO sigue pistas del hardware que pueden aplicarse mal a mi carga de trabajo, mi configuración de energía, mi capa de virtualización o mi estrategia de pinning”. El grafo de culpas se ensancha. Tus runbooks deben ponerse al día.

Hechos e historia: cómo llegamos aquí

Algunos puntos de contexto concretos que ayudan a explicar por qué estamos aquí y por qué esto no va a desaparecer:

  1. Los núcleos asimétricos no son novedad en móviles. Los diseños estilo big.LITTLE normalizaron “núcleos distintos para trabajos distintos” mucho antes de que al escritorio le importara.
  2. La escalada de frecuencia se volvió la norma. DVFS convirtió la “velocidad de la CPU” en un objetivo móvil, y la planificación tuvo que tenerlo en cuenta.
  3. El comportamiento Turbo hizo que el rendimiento por núcleo no fuera uniforme. Incluso núcleos “idénticos” no se comportan igual cuando entran en límites de potencia/temperatura.
  4. SMT complicó el significado de un núcleo. Hyper-Threading (y similares) creó CPUs lógicas que comparten recursos de ejecución, haciendo que las decisiones de colocación importen más.
  5. NUMA ya nos enseñó que la topología importa. Los servidores han vivido con “no todas las CPUs son iguales” debido a la localización de memoria; híbrido trae esa mentalidad a máquinas comunes.
  6. Las mitigaciones de seguridad cambiaron los perfiles de rendimiento. Las mitigaciones de especulación hicieron ciertos patrones intensivos en syscalls más costosos, influyendo en qué núcleos son “mejores” para qué hilos.
  7. La energía se volvió una restricción de primera clase. Presupuestos de potencia en portátiles y centros de datos convirtieron “más rápido” en “lo suficientemente rápido por vatio”.
  8. Los planificadores históricamente operan con rebanadas de tiempo gruesas. El SO ve estados ejecutables y tics de contabilidad; la CPU ve bloqueos y mezcla de instrucciones cada momento.
  9. La virtualización y los contenedores ocultaron la topología a las apps. Cuando apilas hipervisores, cgroups y pinning, la vista del planificador puede distorsionarse a menos que esté cuidadosamente configurada.

Modos de fallo que verás en producción

Thread Director es una respuesta a problemas reales, pero introduce un nuevo conjunto de modos de fallo—mayoritariamente creados por humanos con opiniones firmes y mediciones débiles.

1) Picos de latencia que se correlacionan con trabajo “de fondo”

Ejemplo clásico: tu servicio está mayormente inactivo y luego un trabajo de backup o compresión de logs se inicia. De pronto la latencia tail sube. En hardware híbrido, esto puede ocurrir cuando el planificador malclasifica ráfagas o cuando tus hilos sensibles a la latencia se congestionan en E-cores mientras los P-cores están ocupados con trabajo de rendimiento.

Thread Director puede ayudar, pero solo si el SO lo está usando eficazmente y tus políticas no se lo están obstaculizando.

2) Incidentes “el pinning lo empeoró”

Pinning es tentador: “voy a poner la base de datos en los núcleos rápidos”. Pero fijar en una CPU híbrida a menudo encierra el sistema en un arreglo subóptimo cuando la carga cambia. También puedes fijar por accidente hilos críticos en E-cores—porque tu suposición “CPU 0-7” provenía de otro SKU.

3) La política de energía sabotea la planificación

Windows “Balanced” vs “High performance”, gobernadores de Linux, ajustes de BIOS y demonios de proveedor pueden inclinar el campo de juego. El planificador puede ser brillante y aún así perder si la plataforma restringe el boost de los P-cores o aparca núcleos agresivamente.

4) La virtualización oculta la naturaleza híbrida

Dentro de una VM, el SO invitado puede no ver las clases reales de núcleos. Tu hipervisor podría programar vCPUs en E-cores bajo contención. Ahora depuras latencia de aplicación que en realidad es “la vCPU cayó en el silicio equivocado”.

5) Brechas de observabilidad: no puedes ver lo que la CPU “aconsejó”

Muchos equipos tienen dashboards para uso de CPU, load y run queue. Menos pueden responder: “¿Qué hilos están siendo colocados en E-cores, y por qué?” Cuando no puedes ver la clasificación o los patrones de colocación, tu afinamiento se vuelve superstición.

Guía rápida de diagnóstico

Este es el orden que ahorra tiempo cuando alguien te dice “la máquina con CPU híbrida está lenta y rara”. No te pongas creativo primero. Consigue evidencia primero.

Primero: confirma la topología y si el SO la ve correctamente

  • Identifica tipos de núcleo, estado de SMT y numeración de CPU.
  • Comprueba si la carga está accidentalmente confinada (cgroups, cpuset, límites de contenedor, taskset).
  • Verifica si el planificador del SO soporta conciencia híbrida (versión del kernel, build del SO).

Segundo: decide si es un problema de saturación de CPU o de planificación/colocación

  • Mira la presión del run queue, los cambios de contexto, las migraciones y la utilización por CPU.
  • Busca patrones de “algunas CPUs al máximo, otras inactivas” que gritan problemas de afinidad/cpuset.
  • Comprueba el comportamiento de frecuencia: ¿están los P-cores en boost? ¿están los E-cores haciendo el trabajo pesado?

Tercero: aísla el síntoma a una clase de hilos

  • Identifica a los mayores ofensores por tiempo de CPU y wakeups.
  • Comprueba si hilos sensibles a latencia están terminando en E-cores durante los picos.
  • Si estás en contenedores/VMs, valida la planificación y el pinning del host antes de tocar flags de la app.

Luego: cambia una cosa a la vez

  • Retira el pinning manual antes de añadir más pinning.
  • Prefiere mecanismos de QoS / prioridad soportados por el SO sobre la afinidad rígida, a menos que tengas una razón medida.
  • Si debes fijar, fija por tipo de núcleo intencionalmente, no por “números de CPU que vi en un blog”.

Tareas prácticas: comandos, salidas y la decisión que tomas

El objetivo de estas tareas no es memorizar comandos. Es construir un flujo de trabajo que separe “CPU está ocupada” de “CPU está mal colocada”. Cada tarea incluye: un comando, un ejemplo del tipo de salida que verás, lo que significa y qué hacer a continuación.

Tarea 1: Confirma la topología híbrida y la numeración de CPUs

cr0x@server:~$ lscpu -e=CPU,CORE,SOCKET,NODE,ONLINE,MAXMHZ,MINMHZ
CPU CORE SOCKET NODE ONLINE MAXMHZ MINMHZ
0   0    0      0    yes    5200.0  800.0
1   0    0      0    yes    5200.0  800.0
2   1    0      0    yes    5200.0  800.0
3   1    0      0    yes    5200.0  800.0
8   8    0      0    yes    3800.0  800.0
9   8    0      0    yes    3800.0  800.0
10  9    0      0    yes    3800.0  800.0
11  9    0      0    yes    3800.0  800.0

Qué significa: Buscas agrupaciones de CPUs con techos MAXMHZ distintos; eso suele ser una buena pista de P-cores vs E-cores (no perfecto, pero útil). La numeración de CPU puede no agruparse limpiamente.

Decisión: Construye un mapa explícito de “grupo rápido” vs “grupo lento” para este host; no asumas que CPU0..N son los P-cores.

Tarea 2: Inspecciona caché y detalles de topología

cr0x@server:~$ lscpu | sed -n '1,30p'
Architecture:            x86_64
CPU(s):                  24
Thread(s) per core:      2
Core(s) per socket:      16
Socket(s):               1
L1d cache:               512 KiB (16 instances)
L1i cache:               512 KiB (16 instances)
L2 cache:                20 MiB (10 instances)
L3 cache:                30 MiB (1 instance)
NUMA node(s):            1

Qué significa: Las cachés compartidas importan para los costes de migración. Si los E-cores comparten un cluster L2, mover un hilo dentro/fuera de ese cluster puede cambiar el rendimiento.

Decisión: Si tu carga es sensible a caché, evita churn agresivo de afinidad. Prefiere colocación estable y menos migraciones.

Tarea 3: Comprueba el kernel y la línea base del planificador

cr0x@server:~$ uname -r
6.5.0-21-generic

Qué significa: El soporte de planificación híbrida depende mucho de la generación del kernel y parches del proveedor.

Decisión: Si estás en un kernel antiguo y persigues rarezas híbridas, actualizar no es “agradable de tener”. Es el primer intento real de arreglo.

Tarea 4: Verifica el gobernador de frecuencia y la política

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

Qué significa: schedutil acopla decisiones de frecuencia a señales de utilización del planificador. Otros governors pueden ser más agresivos o más conservadores.

Decisión: Si ves powersave en una máquina sensible a latencia, arregla la política antes de tocar el afinamiento de la aplicación.

Tarea 5: Observa frecuencias reales bajo carga (turbostat)

cr0x@server:~$ sudo turbostat --Summary --quiet --interval 1 --num_iterations 3
Avg_MHz  Busy%  Bzy_MHz  TSC_MHz  PkgTmp  PkgWatt
  812     6.12    4172    3000      52     12.40
 1190    18.55    4388    3000      58     21.10
  945     9.80    4021    3000      55     14.80

Qué significa: Bzy_MHz es la frecuencia efectiva mientras está ocupada. Si nunca sube, podrías estar limitado por energía, térmica o mal configurado en BIOS.

Decisión: Si el boost está capado, deja de culpar a la planificación hasta que arregles las limitaciones de plataforma.

Tarea 6: Identifica desequilibrio por CPU (algunos núcleos al máximo, otros inactivos)

cr0x@server:~$ mpstat -P ALL 1 2
Linux 6.5.0-21-generic (server)  01/10/2026  _x86_64_  (24 CPU)

12:00:01 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:02 AM  all   38.1  0.0   4.2  0.1     0.0  0.3   0.0    57.3
12:00:02 AM    0   92.0  0.0   3.0  0.0     0.0  0.0   0.0     5.0
12:00:02 AM    8    6.0  0.0   1.0  0.0     0.0  0.0   0.0    93.0

Qué significa: CPU0 está trabajando mucho mientras CPU8 está básicamente dormida. Eso es o afinidad, restricciones de cpuset o un cuello de botella de un solo hilo.

Decisión: Si esperabas paralelismo, inspecciona afinidad y cpusets antes de cambiar código.

Tarea 7: Comprueba la presión del run queue y cambios de contexto

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
 1  0      0 521312  64288 882112    0    0     1     3  812 1200 12  3 85  0
 8  0      0 519040  64288 881920    0    0     0     0 1050 9800 62  6 32  0

Qué significa: r saltando a 8 significa muchos hilos ejecutables. cs subiendo rápido sugiere cambios de contexto pesados; en híbrido, eso a menudo significa migraciones y contención.

Decisión: Si los hilos ejecutables exceden la capacidad efectiva de CPU disponible, deja de perseguir “colocación” y empieza a buscar saturación o contención de locks.

Tarea 8: Ver el comportamiento de migraciones y balance del planificador (perf sched)

cr0x@server:~$ sudo perf sched record -- sleep 5
[ perf sched record: Woken up 7 times to write data ]
[ perf sched record: Captured and wrote 2.113 MB perf.data (23619 samples) ]
cr0x@server:~$ sudo perf sched latency --sort=max | sed -n '1,12p'
    Task                  |   Runtime ms  | Switches | Avg delay ms | Max delay ms
  nginx:worker-01         |      310.422  |     2381 |       0.122  |       7.843
  backup:compress         |      882.001  |     1140 |       0.410  |      21.554

Qué significa: Un Max delay alto para hilos sensibles a latencia apunta a demoras de planificación (esperas en la run queue). Si las malas demoras coinciden con tareas de rendimiento, tienes problemas de priorización y colocación.

Decisión: Considera ajustes de prioridad/QoS o aislar trabajos ruidosos de fondo en E-cores en lugar de fijar todo “importante” a P-cores.

Tarea 9: Comprueba la afinidad actual de procesos/hilos

cr0x@server:~$ pidof nginx
2140
cr0x@server:~$ taskset -cp 2140
pid 2140's current affinity list: 0-7

Qué significa: Nginx está confinado a CPUs 0-7. Si 0-7 no son todos P-cores en este sistema, podrías haberlo puesto accidentalmente en un conjunto mixto o lento.

Decisión: Elimina la afinidad general a menos que puedas demostrar que ayuda. Si debes restringir, hazlo con conocimiento de las clases de núcleo.

Tarea 10: Inspecciona restricciones de cgroup CPU (común en contenedores)

cr0x@server:~$ cat /sys/fs/cgroup/cpuset.cpus.effective
0-11
cr0x@server:~$ cat /sys/fs/cgroup/cpu.max
80000 100000

Qué significa: cpu.max sugiere una cuota (80% de una CPU en este ejemplo por período), y cpuset limita las CPUs disponibles. Híbrido complica el concepto de “CPU efectiva”: 4 E-cores no equivalen a 4 P-cores para latencia.

Decisión: Para SLOs de latencia, prefiere fijar cargas en P-cores conocidos vía cpusets en lugar de confiar solo en cuotas.

Tarea 11: Comprueba la distribución de IRQs (las interrupciones pueden robarte los mejores núcleos)

cr0x@server:~$ cat /proc/interrupts | sed -n '1,8p'
           CPU0       CPU1       CPU2       CPU3
  24:    812340          0          0          0  IR-PCI-MSI 524288-edge  eth0-TxRx-0
  25:         0     792110          0          0  IR-PCI-MSI 524289-edge  eth0-TxRx-1

Qué significa: Si todas las interrupciones de alta tasa caen en CPU0 y CPU0 es un P-core, podrías estar desperdiciando tu mejor núcleo en trabajo de IRQ.

Decisión: Considera ajustar la afinidad de IRQ para que los hilos de aplicación sensibles a latencia obtengan P-cores limpios.

Tarea 12: Observa wakeups por hilo y colocación de CPU (top + ps)

cr0x@server:~$ top -H -p 2140 -b -n 1 | sed -n '1,20p'
top - 00:00:10 up 10 days,  2:14,  1 user,  load average: 3.12, 2.98, 2.70
Threads:  18 total,   2 running,  16 sleeping,   0 stopped,   0 zombie
%Cpu(s):  35.0 us,  4.0 sy,  0.0 ni, 61.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   2145 www-data  20   0  125352  11420   5120 R  62.3   0.2   1:22.10 nginx: worker
   2146 www-data  20   0  125352  11420   5120 R  58.9   0.2   1:18.44 nginx: worker
cr0x@server:~$ ps -L -o pid,tid,psr,cls,rtprio,pri,ni,comm -p 2140 | sed -n '1,8p'
  PID   TID PSR CLS RTPRIO PRI  NI COMMAND
 2140  2140   0  TS      -  19   0 nginx
 2140  2145   2  TS      -  19   0 nginx
 2140  2146   3  TS      -  19   0 nginx

Qué significa: PSR muestra en qué CPU corrió el hilo por última vez. Si tus workers rebotan entre P- y E-cores rápidamente durante picos de latencia, lo verás aquí con el tiempo.

Decisión: Si el rebote se correlaciona con mala latencia tail, busca colocación estable: arregla cargas competidoras, ajusta prioridades o usa cpusets dirigidos.

Tarea 13: Comprueba si el proceso está siendo limitado (estadísticas de cgroup CPU)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 823412341
user_usec  792112000
system_usec 31200341
nr_periods  12490
nr_throttled 340
throttled_usec 129003210

Qué significa: Si nr_throttled y throttled_usec suben durante incidentes, tu “problema de planificación” es en realidad un problema de cuota.

Decisión: Aumenta la cuota, mueve la carga o aíslala en núcleos dedicados. No afines Thread Director cuando literalmente estás negando tiempo de CPU.

Tarea 14: Valida cpuset vs “lo que pensabas que estaba fijado” (systemd)

cr0x@server:~$ systemctl show nginx --property=AllowedCPUs --property=CPUQuota
AllowedCPUs=0-7
CPUQuota=80%

Qué significa: systemd puede imponer restricciones sin que nadie lo recuerde. Combina eso con topología híbrida y tienes experimentos de rendimiento accidentales.

Decisión: Alinea la política de CPU de la unidad de servicio con tu intención: o elimina restricciones o configúralas explícitamente por clase de núcleo.

Tarea 15: Detecta contención de locks que se disfraza de “lentitud de E-core”

cr0x@server:~$ sudo perf top -p 2140 --stdio --delay 2 | sed -n '1,18p'
Samples: 1K of event 'cycles', 4000 Hz, Event count (approx.): 250000000
Overhead  Shared Object        Symbol
  18.22%  nginx               ngx_http_process_request_line
  11.40%  libc.so.6           __pthread_mutex_lock
   9.10%  nginx               ngx_http_upstream_get_round_robin_peer

Qué significa: Si mucho tiempo está en mutex locks, mover hilos a P-cores no arreglará la contención subyacente. Podría incluso acentuarlo.

Decisión: Trata la contención como causa raíz separada: arregla el lock, reduce el estado compartido o escala horizontalmente. No fijes más.

Tarea 16: Verifica el pinning de CPU de la capa de virtualización (lado host)

cr0x@server:~$ virsh vcpupin appvm
VCPU: CPU Affinity
----------------------------------
0: 0-23
1: 0-23
2: 0-23
3: 0-23

Qué significa: La VM puede ejecutarse en cualquier CPU del host. Bajo contención, puede aterrizar en E-cores, y tu SO invitado jurará que todo está bien.

Decisión: Si la VM aloja servicios sensibles a latencia, fija las vCPUs a P-cores en el hipervisor, no dentro del invitado.

Broma corta #2: Si no puedes reproducir el problema, no te preocupes—producción lo reproducirá gustosamente a las 2 a.m.

Tres microhistorias corporativas desde las trincheras de la planificación

Microhistoria #1: El incidente causado por una suposición errónea

Una empresa SaaS mediana desplegó nuevas estaciones de trabajo de desarrollador que también se usaban como runners de CI informales. CPUs híbridas, mucha RAM, “debería ser más rápido”. En una semana, la pipeline de compilación empezó a mostrar trabajos aleatoriamente lentos: algunos terminaban en diez minutos, otros en veinticinco, mismo commit, misma imagen de contenedor.

La primera suposición fue familiar: “Docker está robando CPU”. El equipo apretó cuotas de CPU en cgroups para “hacerlo justo”. La variación empeoró. Los tiempos máximos de compilación se dispararon y los ingenieros empezaron a relanzar trabajos hasta tener suerte—una costosa forma de ingeniería de confiabilidad.

El problema real era más sutil: su servicio runner tenía una unidad systemd con AllowedCPUs=0-7 heredada de una guía de afinamiento anterior. En las máquinas previas no híbridas, CPUs 0-7 eran “la mitad de la máquina, todavía bien”. En el nuevo SKU híbrido, 0-7 incluían una mezcla que favorecía fuertemente los núcleos más lentos. Bajo compilaciones con ráfagas, el planificador hizo lo que pudo, pero el servicio ya se había cercado en el pasto equivocado.

Arreglarlo no fue heroico. Eliminaron la restricción, midieron de nuevo y luego reintrodujeron una política deliberada: trabajos de fondo pueden usar E-cores; tareas interactivas sensibles a latencia obtienen P-cores. El postmortem no culpó al SO. Culpó la suposición de que la numeración de CPU es estable entre generaciones de hardware. Con razón.

Microhistoria #2: La optimización que salió mal

Un equipo fintech operaba un servicio de precios de baja latencia con presupuestos estrictos de p99. Leyeron que “la migración de hilos daña la caché” y decidieron fijar los hilos trabajadores principales a P-cores. También fijaron hilos de logging y métricas “fuera del camino” en E-cores. Parecía limpio en el papel.

Durante unos días, la mediana de latencia mejoró ligeramente. Luego p99 empezó a dispararse en la apertura de mercado. El servicio no estaba saturado globalmente. Aún así, las peores peticiones expiraban. Los ingenieros añadieron más pins y más aislamiento. Los picos persistieron. El comandante del incidente lo describió como “el planificador está encantado”, que no es un diagnóstico accionable.

El problema era que su servicio tenía un patrón en ráfagas: el hilo principal delegaba trabajo a hilos auxiliares para criptografía y compresión. Esos auxiliares no estaban fijados y con frecuencia eran programados en E-cores debido a la presión de colocación del sistema. Cuando los trabajadores fijados en P-cores esperaban los resultados de los auxiliares, la ruta crítica se alargaba. El acto mismo de fijar redujo la capacidad del planificador para co-ubicar hilos cooperativos en los mejores núcleos.

La optimización contraproducente fue la creencia de que “los hilos críticos son los llamados ‘worker’”. En realidad, la ruta crítica incluía auxiliares, tiempo en kernel y actividad ocasional de GC. La solución fue dejar de microgestionar con afinidad, reactivar la flexibilidad del planificador y aplicar un enfoque toscamente correcto: mantener la máquina libre de vecinos ruidosos, ajustar prioridades de forma sensata y asegurar que los P-cores estuvieran disponibles cuando la CPU señalara la necesidad.

Microhistoria #3: La práctica aburrida pero correcta que salvó el día

Una empresa de medios operaba una flota de servidores de codificación. Las CPUs híbridas entraron en la flota gradualmente, y ahí es donde el hardware mixto realmente pone a prueba la madurez operativa. El equipo tenía una política que suena aburrida y por eso es rara: cada nuevo tipo de hardware obtenía una huella topológica en la gestión de configuración—recuento de núcleos, SMT, bandas de frecuencia máximas y un mapeo cpuset probado.

Cuando llegó un nuevo lote, una actualización de BIOS del proveedor cambió el comportamiento de energía y alteró ligeramente las características de boost. No hubo caída. Pero sus dashboards canario marcaron “varianza en tiempo de pared de codificación +12% en nodos nuevos”. Esa alerta no era sobre uso de CPU; era sobre la distribución de tiempo de finalización de trabajos. Otra vez: observabilidad aburrida y correcta.

Porque tenían una huella, pudieron validar rápidamente que su política “trabajos de throughput en E-cores primero” aún coincidía con la realidad. Ejecutaron turbostat durante trabajos canario, confirmaron que los P-cores no estaban haciendo boost como se esperaba y revirtieron la política de BIOS en lugar de reescribir su afinamiento del planificador.

Lo mejor: nadie discutió por sensaciones. Discutieron por evidencia. La flota se mantuvo estable y el informe del incidente ocupó una página—un lujo subestimado.

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

Error 1: “Algunos núcleos están inactivos, así que tenemos capacidad”

Síntomas: Uso global de CPU moderado, pero picos de latencia; mpstat muestra carga desigual por CPU.

Causa raíz: Afinidad o restricciones de cpuset confinan hilos calientes a un subconjunto (a menudo incluyendo E-cores), dejando otras CPUs sin usar.

Solución: Audita taskset, AllowedCPUs de systemd, cpusets de contenedores. Elimina restricciones primero; reintrodúcelas solo con mapeo explícito P/E.

Error 2: “Fijar a P-cores siempre mejora la latencia”

Síntomas: p50 mejora, p99 empeora; el rendimiento varía en ráfagas.

Causa raíz: Fijaste parte de la ruta crítica, pero auxiliares y trabajo del kernel siguen en otros sitios. También incrementaste la contención en el conjunto fijado y redujiste la flexibilidad del planificador.

Solución: Prefiere prioridad/QoS sobre afinidad rígida. Si debes fijar, fija el conjunto cooperante entero (y valida con perf sched latency).

Error 3: “Es culpa de Thread Director”

Síntomas: Tras una renovación de hardware, aparece “planificación rara”; los equipos culpan al CPU/SO.

Causa raíz: Política de energía, límites térmicos, ajustes de BIOS o firmware están capando el boost, haciendo que los P-cores se comporten menos “P”.

Solución: Mide frecuencias reales (turbostat) bajo carga representativa. Arregla las limitaciones de plataforma antes de perseguir afinamiento del planificador.

Error 4: “Dentro de la VM todo es idéntico”

Síntomas: El SO invitado muestra uso de CPU estable; la latencia de la app oscila; la contención del host se correlaciona con picos.

Causa raíz: Las vCPUs se programan en E-cores o CPUs host contended; el invitado no ve la topología híbrida.

Solución: Fija o prioriza vCPUs a nivel del hipervisor. Trata híbrido como un problema de planificación a nivel host.

Error 5: “La cuota está bien porque la CPU promedio es baja”

Síntomas: Acantilados periódicos de latencia; cpu.stat muestra throttling; las ráfagas parecen “hiccups” del planificador.

Causa raíz: La cuota de cgroup limita las ráfagas, dañando especialmente a hilos sensibles a latencia.

Solución: Incrementa la cuota o elimínala; aísla cargas ruidosas con cpusets; usa métricas de throttling como indicadores SLO de primera clase.

Error 6: “Los E-cores son solo para trabajo basura”

Síntomas: Trabajos de throughput se quedan sin recursos; la potencia del sistema es alta; P-cores están ocupados con tareas de fondo.

Causa raíz: La política manda demasiado a P-cores, ignorando que los E-cores pueden ofrecer excelente throughput/watt.

Solución: Pon trabajo de throughput predecible en E-cores intencionalmente, dejando P-cores para trabajo con ráfagas/sensible a latencia. Valida con distribuciones de tiempo de finalización, no solo con %CPU.

Listas de verificación / plan paso a paso

Paso a paso: integrar un host híbrido en una flota de producción

  1. Inventario de topología: registra modelo de CPU, conteo de núcleos, SMT, bandas de frecuencia máximas, nodos NUMA, disposición de cachés.
  2. Base de soporte del SO: confirma build del kernel/SO y que no estás en una versión antigua del planificador “funciona en mi laptop”.
  3. Establece una política de energía intencional: elige un governor/plan que coincida con la clase de servicio (latencia vs throughput vs batería).
  4. Ejecuta canarios con carga real: un CPU burn sintético no reproducirá el comportamiento de clasificación.
  5. Mide la varianza: controla p50/p95/p99 para latencia de peticiones o tiempo de trabajo; los problemas híbridos aparecen como varianza.
  6. Comprueba throttling: cuotas de cgroup y límites térmicos deben observarse, no asumirse.
  7. Solo entonces considera la afinidad: trata el pinning como una herramienta de último recurso, no como la primera respuesta.

Lista de verificación: antes de fijar cualquier cosa

  • ¿Tienes un mapeo P-core/E-core verificado para este SKU de host en particular?
  • ¿Has confirmado que la carga no está siendo limitada por cuota?
  • ¿Has medido migraciones y retraso en run queue con perf sched?
  • ¿Están las IRQs aterrizando en los núcleos que piensas “reservar”?
  • ¿Reducirá el pinning la habilidad del planificador para co-ubicar hilos cooperantes?

Lista de verificación: si ejecutas contenedores en CPUs híbridas

  • Prefiere cpusets para servicios críticos: asigna P-cores conocidos para pods/servicios sensibles a latencia.
  • Evita cuotas de CPU estrictas para servicios con ráfagas; las cuotas hacen que “ráfagas cortas” sean caras.
  • Expón la topología a las decisiones de orquestación en la capa correcta (host primero, luego orquestador, luego carga).
  • Registra la topología por nodo en tu inventario para que “CPU 0-7” no se convierta en sabiduría tribal.

Preguntas frecuentes

¿Reemplaza Thread Director al planificador del SO?

No. Proporciona orientación (pistas impulsadas por telemetría). El SO sigue tomando la decisión final basada en política, equidad, prioridades, cgroups y restricciones.

¿Por qué no puede el SO resolverlo por sí mismo?

PUEDE—eventualmente, aproximadamente. La CPU ve comportamiento de grano fino (bloqueos, mezcla de instrucciones) más rápido de lo que el SO puede inferir por contabilidad por rebanadas de tiempo. Híbrido hace que “eventualmente” sea demasiado lento y “aproximadamente” demasiado caro.

¿Es esto solo un problema de Windows o de Linux?

Es un problema del híbrido. Diferentes versiones de SO lo manejan de forma distinta, pero el reto central—elegir entre núcleos asimétricos bajo comportamiento de hilos cambiante—existe en todas partes.

¿Debo desactivar los E-cores en servidores?

Usualmente no. Desactivar E-cores es un instrumento contundente que cambia complejidad por silicio desperdiciado. Hazlo solo si tienes un requisito de latencia medido que no puedes cumplir por otros medios y ya agotaste soluciones sensatas de planificación/política.

¿Debo fijar mi base de datos a P-cores?

Sólo si has probado que los hilos críticos son consistentemente de baja latencia y las migraciones te están perjudicando. Muchas bases de datos se benefician más de reducir la contención, colocar IRQs de forma estable y una política de energía predecible que del pinning rígido.

¿Por qué las compilaciones varían tanto en máquinas híbridas?

Las compilaciones son ráfagas y mixtas: compilación (CPU-intensiva), linking (a menudo cuello de botella mono-hilo), compresión y esperas I/O. Si fases clave caen en E-cores o están limitadas por cuota, el tiempo de pared oscila.

¿Cuál es la señal más rápida de que es un problema de cuota/throttling, no de Thread Director?

/sys/fs/cgroup/cpu.stat. Si el tiempo throttled sube durante incidentes, no estás tratando con “colocación”. Estás tratando con negación de tiempo de CPU.

¿Cómo se relacionan las interrupciones con la planificación híbrida?

Las interrupciones pueden consumir tus mejores núcleos y crear jitter. Si las IRQs de la NIC se quedan en un P-core, podrías estar intercambiando latencia de aplicación por manejo de paquetes sin darte cuenta.

¿Cuál es la estrategia predeterminada más segura para cargas mixtas?

Dale espacio al planificador para trabajar, mantén una política de energía sensata, aisla trabajo ruidoso de fondo (a menudo en E-cores) y mide latencia tail y varianza. Usa afinidad de forma quirúrgica, no emocional.

Conclusión: qué hacer la próxima semana

Thread Director no es que la CPU “tome el control”. Es la CPU admitiendo que tiene mejores datos en tiempo real que el SO y ofreciendo esos datos para que la planificación en núcleos asimétricos deje de ser una conjetura. La ganancia no es solo velocidad. Es predictibilidad—y la predictibilidad es lo que mantiene los pagers tranquilos.

Pasos prácticos siguientes:

  1. Elige un host híbrido y crea una huella topológica: grupos de núcleos, techos de frecuencia y puntos calientes de IRQ.
  2. Añade dos dashboards: varianza de tiempo de trabajo/ejecución y throttling de CPU. La CPU promedio es una mentirosa; las distribuciones son adultas.
  3. Audita tu flota en busca de afinidad oculta: systemd AllowedCPUs, uso de taskset, cpusets de contenedores.
  4. Cuando veas picos de latencia, ejecuta la guía rápida de diagnóstico en orden. No improvises.
  5. Si debes fijar, hazlo con intención: por clase de núcleo verificada, con medición y con plan de reversión.

La CPU ahora aconseja. No tienes que obedecerla ciegamente—pero sí debes dejar de fingir que no te está hablando.

← Anterior
Migración de dominio WordPress sin perder SEO: 301, canónicos, sitemaps y cero drama
Siguiente →
Estilos de impresión para documentación que no te avergüencen

Deja un comentario