1. Qué es la afinidad de caché
La afinidad de caché (cache affinity) es la tendencia de un proceso a ejecutarse en el mismo procesador (o núcleo) donde se ejecutó anteriormente, para aprovechar los datos que quedaron en la caché de ese procesador.
Cuando un proceso se ejecuta en un núcleo, sus datos (instrucciones, variables, estructuras) se cargan en la caché L1, L2 y L3 de ese núcleo. Si el proceso se ejecuta de nuevo en el mismo núcleo, esos datos ya están en la caché y se accede a ellos rápidamente. Si se migra a otro núcleo, la caché está fría (cold) y hay que recargar todos los datos, lo que causa cache misses.
La afinidad de caché es especialmente importante para procesos con grandes working sets (conjuntos de datos que usan frecuentemente). Un proceso que accede a 1 MB de datos tendrá muchos cache misses si se migra a otro núcleo.
2. Por qué importa la afinidad
El costo de perder la afinidad de caché puede ser significativo:
| Nivel de caché | Tiempo de acceso | Costo de miss |
|---|---|---|
| L1 | 1-4 ciclos | ~10-40 ciclos (acceso a L2) |
| L2 | 10-20 ciclos | ~100 ciclos (acceso a L3) |
| L3 | 30-50 ciclos | ~200-300 ciclos (acceso a RAM) |
Un proceso que pierde su caché L1 puede tardar 10-40 veces más en acceder a sus datos. Si el proceso accede a la caché miles de veces por milisegundo, el impacto es enorme.
Estudios han mostrado que la migración de un proceso entre núcleos puede causar una degradación del rendimiento del 10-30 %, dependiendo del tamaño del working set y del patrón de acceso.
La afinidad de caché también afecta a la energía: acceder a la caché consume menos energía que acceder a la RAM. Mantener la afinidad reduce el consumo de energía.
3. Qué es el balanceo de carga
El balanceo de carga (load balancing) es el proceso de distribuir los procesos entre los procesadores para que todos tengan una carga similar. El objetivo es evitar que un procesador esté sobrecargado mientras otro está ocioso.
El balanceo de carga es necesario porque:
- Los procesos llegan en momentos distintos: un procesador puede recibir muchos procesos mientras otro recibe pocos.
- Los procesos tienen duraciones distintas: un procesador puede tener un proceso largo mientras otro tiene varios cortos.
- Los procesos se bloquean: un procesador puede tener todos sus procesos bloqueados en E/S mientras otro tiene procesos listos.
Sin balanceo, un procesador puede estar al 100 % mientras otro está al 0 %. El throughput del sistema se reduce porque los procesos en el procesador ocupado esperan más.
4. El trade-off: afinidad vs balanceo
La afinidad y el balanceo compiten:
- Más afinidad: mantener el proceso en el mismo núcleo. Mejor rendimiento por proceso, pero puede causar desbalanceo.
- Más balanceo: migrar el proceso a un núcleo menos cargado. Mejor distribución, pero pérdida de caché.
El equilibrio depende de:
- El tamaño del working set: si es grande, la afinidad es más importante.
- El desequilibrio de carga: si es grande, el balanceo es más importante.
- La frecuencia de migración: migrar con poca frecuencia preserva la afinidad; migrar con mucha frecuencia mejora el balanceo.
No hay un punto fijo de equilibrio. El sistema debe ajustar dinámicamente la frecuencia de migración según la carga de trabajo y el desequilibrio.
5. Cómo migran los procesos
La migración de un proceso de un núcleo a otro implica:
- Seleccionar el proceso a migrar. Se elige un proceso de la cola del núcleo más cargado. seleccionar
- Quitarlo de la cola origen. Se elimina de la cola del núcleo origen. quitar
- Añadirlo a la cola destino. Se añade a la cola del núcleo menos cargado. añadir
- Actualizar la afinidad. Se actualiza la afinidad del proceso para que prefiera el nuevo núcleo. afinidad
La migración tiene un costo: el proceso pierde su caché y debe recargar sus datos en el nuevo núcleo. Por eso, no se migra un proceso si el beneficio del balanceo no compensa el costo de la migración.
Linux usa una heurística: solo migra un proceso si el desequilibrio de carga es significativo (por ejemplo, si un núcleo tiene el doble de carga que otro).
6. Tipos de balanceo
| Tipo | Cuándo se ejecuta | Ventaja | Desventaja |
|---|---|---|---|
| Periódico | Cada cierto tiempo (por ejemplo, cada 100 ms). | Simple, predecible. | Puede ser demasiado frecuente o infrecuente. |
| Por evento | Cuando un núcleo queda ocioso. | Reacciona rápido al desequilibrio. | Puede causar migraciones innecesarias. |
| Por umbral | Cuando el desequilibrio supera un umbral. | Solo migra cuando es necesario. | Hay que elegir el umbral. |
Linux usa una combinación: balanceo periódico (cada cierto tiempo) y por evento (cuando un CPU queda ocioso). El balanceo por evento es más reactivo y se ejecuta cuando un CPU no tiene trabajo.
7. Dominios de planificación
Los dominios de planificación (scheduling domains) son grupos de CPUs que se balancean entre sí. Linux organiza los CPUs en una jerarquía de dominios: primero se balancea entre CPUs del mismo socket (que comparten caché L3), luego entre sockets del mismo nodo NUMA, y finalmente entre nodos NUMA.
La jerarquía permite balancear de forma eficiente: primero se intenta balancear dentro del mismo socket (bajo costo, porque comparten caché), y solo si es necesario se balancea entre sockets (mayor costo).
Cada dominio tiene sus propios parámetros: intervalo de balanceo, umbral de desequilibrio, etc. Los dominios más cercanos (mismo socket) se balancean con más frecuencia que los más lejanos (entre nodos NUMA).
Al balancear primero dentro del mismo socket, Linux minimiza las migraciones costosas (entre sockets o nodos NUMA). La mayoría de las migraciones son locales y tienen bajo costo.
8. Cómo lo hace Linux
Linux implementa la afinidad y el balanceo de la siguiente manera:
- Afinidad suave: el planificador intenta mantener un proceso en el mismo CPU, pero no lo garantiza. Si el CPU está ocupado, puede ejecutarlo en otro.
- Afinidad dura: el usuario o el proceso puede fijar un proceso a un CPU específico (con
tasksetosched_setaffinity). El proceso solo se ejecuta en ese CPU. - Balanceo periódico: cada cierto tiempo, el planificador balancea la carga entre los CPUs de un dominio.
- Balanceo por evento: cuando un CPU queda ocioso, intenta robar un proceso de otro CPU.
- Cache-aware: el planificador considera la afinidad de caché al decidir si migrar un proceso.
Las herramientas taskset y numactl permiten controlar la afinidad desde la línea de comandos.
9. Simulador interactivo
Este simulador muestra el trade-off entre afinidad y balanceo. Con afinidad activada, los procesos se mantienen en el mismo CPU (mejor caché, pero posible desbalanceo). Sin afinidad, se distribuyen libremente (mejor balanceo, pero pérdida de caché):
Simulador · afinidad vs balanceo
CPU 0
CPU 1
| Proceso | Llegada | Ráfaga | CPU | Inicio | Fin | Retorno |
|---|---|---|---|---|---|---|
10. Resumen
- La afinidad de caché favorece mantener un proceso en el mismo núcleo.
- El balanceo de carga distribuye los procesos entre los núcleos.
- Afinidad y balanceo compiten: más de uno significa menos del otro.
- La migración tiene un costo: pérdida de caché.
- Los dominios de planificación organizan los CPUs en jerarquía para balancear eficientemente.
- Linux usa afinidad suave y balanceo periódico/por evento.
- La afinidad dura se controla con taskset y sched_setaffinity.
11. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. ¿Qué es la afinidad de caché?
La tendencia de un proceso a ejecutarse en el mismo procesador para aprovechar la caché.
2. ¿Por qué importa la afinidad?
Porque migrar un proceso pierde la caché, causando cache misses y degradación del rendimiento.
3. ¿Qué es el balanceo de carga?
Distribuir los procesos entre los procesadores para equilibrar la carga.
4. ¿Cuál es el trade-off principal?
Afinidad (mejor rendimiento por proceso) vs balanceo (mejor distribución).
5. ¿Qué es la migración de un proceso?
Mover un proceso de un núcleo a otro para equilibrar la carga.
6. ¿Qué tipos de balanceo existen?
Periódico, por evento y por umbral.
7. ¿Qué son los dominios de planificación?
Grupos de CPUs que se balancean entre sí, organizados en jerarquía.
8. ¿Qué es la afinidad suave?
El planificador intenta mantener el proceso en el mismo CPU pero no lo garantiza.
9. ¿Qué es la afinidad dura?
El proceso se fija a un CPU específico y solo se ejecuta ahí.
10. ¿Cómo se controla la afinidad dura en Linux?
Con taskset o sched_setaffinity.
Para practicar
- Ejecuta el simulador con y sin afinidad y compara los Gantt.
- Diseña un caso donde el desbalanceo sea evidente sin afinidad.
- Explica por qué la jerarquía de dominios reduce el costo del balanceo.
- Implementa la afinidad suave en el simulador por núcleo.
- Investiga cómo taskset funciona en Linux.
- Explica el trade-off entre migración frecuente y afinidad.