17 Unidad 7 — Memoria Virtual: El costo oculto: rendimiento, localidad y thrashing
Unidad 7 — Memoria Virtual
En la sesión anterior establecimos el qué y el cómo de la memoria virtual: una abstracción que provee a cada proceso un espacio de direcciones privado y lineal. La paginación por demanda es el mecanismo que hace esto posible de manera eficiente. Sin embargo, esta eficiencia introduce una nueva dimensión que todo profesional de sistemas debe dominar: el rendimiento.
Un acceso a memoria ya no tiene un coste constante. Puede ser increíblemente rápido (un acierto de caché) o catastróficamente lento (un fallo de página mayor). Esta sesión se enfoca en entender las fuerzas que gobiernan este rendimiento. Exploraremos cómo un principio fundamental, la localidad de referencia, dicta el comportamiento del sistema, y qué sucede cuando este principio se ignora, llevando al colapso del rendimiento conocido como thrashing.
17.1 1. Paginación por demanda: una optimización con coste
Como vimos, la paginación por demanda es la estrategia de no cargar una página en memoria física hasta que se produce un fallo al intentar accederla. Esto ofrece grandes beneficios, como un inicio más rápido de los procesos y un menor uso de RAM.
Sin embargo, introduce una latencia variable y potencialmente alta. El fallo de página no es un error, es una interrupción que transfiere el control al kernel para que solucione la ausencia de la página. El coste de esta operación define el rendimiento de nuestro programa:
- Fallo menor: la página ya está en RAM (por ejemplo, en el page cache o como parte de otro proceso en copy-on-write) pero no está mapeada en la tabla de páginas del proceso; el kernel solo actualiza las estructuras de memoria. Es rápido.
- Fallo mayor: la página no está en RAM y debe leerse desde almacenamiento secundario (disco o SSD). Implica I/O y es órdenes de magnitud más lento.
Impacto para el ingeniero: un alto número de fallos de página mayores indica problemas de rendimiento; el objetivo es minimizar los fallos que requieren acceso a disco.
17.2 2. El principio de localidad: el factor clave del rendimiento
¿Por qué los sistemas de memoria jerárquicos (cachés, RAM, disco) funcionan tan bien? La respuesta es el principio de localidad, que observa que los programas no acceden a la memoria de forma completamente aleatoria.
- Localidad temporal: Si un programa accede a una dirección de memoria, es muy probable que vuelva a acceder a esa misma dirección en un futuro cercano (ej. una variable dentro de un bucle).
- Localidad espacial: Si un programa accede a una dirección de memoria, es muy probable que en un futuro cercano acceda a direcciones cercanas (ej. recorrer los elementos de un array).
Impacto para el programador: escribir código con buena localidad (espacial y temporal) es la optimización de memoria más efectiva; reduce fallos de página y aumenta aciertos de caché.
17.3 3. Políticas de reemplazo de página: la lucha por los frames
Cuando falta memoria física libre tras un fallo de página, el sistema debe elegir una página “víctima” a desalojar. Las políticas influyen directamente en el rendimiento:
LRU (Least Recently Used) — La política óptima (y teóricamente implementable) es una manifestación directa de la localidad temporal. Si tenemos que quitar una página, quitemos la que lleva más tiempo sin usarse, ya que, por localidad, es la menos probable que se necesite pronto. Su implementación pura es demasiado costosa para los sistemas reales.
FIFO (First-In, First-Out) — Una política simple y “tonta”. Quita la página que más tiempo lleva en memoria, sin importar si se está usando activamente. Es un mal algoritmo porque ignora por completo la localidad.
Algoritmo del Reloj / Segunda oportunidad — práctica común: Los sistemas operativos reales utilizan una aproximación a LRU. Asocian un bit de referencia a cada página. El hardware pone este bit a 1 cada vez que la página es accedida. Cuando el SO necesita liberar un marco, busca una página con el bit en 0. Si encuentra una página con el bit en 1 (significa “ha sido usada recientemente”), le da una “segunda oportunidad”: pone su bit a 0 y continúa buscando. Es una forma simple y eficiente de distinguir entre páginas usadas recientemente y las que no.
Impacto para el analista: entender que el SO intentará mantener en memoria las páginas activas del proceso; mala localidad del programa fuerza decisiones subóptimas del SO.
17.4 4. El conjunto de trabajo (working set)
El conjunto de trabajo de un proceso es el conjunto de páginas a las que ha accedido en un pasado reciente (en una “ventana” de tiempo Δ). Es la medida práctica de la localidad de un programa.
- Un programa con buena localidad tendrá un conjunto de trabajo pequeño y estable.
- Un programa con mala localidad tendrá un conjunto de trabajo grande e inestable.
17.5 5. Thrashing: cuando la localidad falla
El thrashing es un estado de colapso del rendimiento que ocurre cuando los procesos no disponen de marcos suficientes para mantener sus conjuntos de trabajo.
El sistema entra en un círculo vicioso:
- El proceso necesita la página A, pero la memoria está llena.
- El SO desaloja la página B.
- Poco después, el proceso necesita B; el SO desaloja la página C.
- Y así sucesivamente…
El sistema pasa la mayor parte del tiempo moviendo páginas entre RAM y disco (paginando) y muy poco tiempo ejecutando trabajo útil.
17.5.1 Impacto práctico
- Para el analista de sistemas: thrashing aparece como alta actividad de swap-in/swap-out (herramientas como
vmstatosarlo muestran claramente), CPU ocupada en paginación y throughput muy bajo. - Para el programador: indica patrones de acceso con conjuntos de trabajo mayores que la memoria disponible; la solución suele ser rediseñar el algoritmo para mejorar localidad.