← Gestión de Memoria Bloque 8 · Reemplazo de páginas · Tema 39 de 52

Tema 39

Simulador de algoritmos de reemplazo

El cierre del bloque: los cuatro algoritmos de reemplazo —FIFO, LRU, reloj y óptimo— compiten sobre la misma cadena de referencias y los mismos marcos. Comparar exige condiciones idénticas, y mirar solo el total de fallos engaña: hay que ver el detalle de cada acceso, la cota que impone el óptimo y los casos patológicos. En este tema los compararás en C y en un simulador que traza la tabla de fallos, dibuja la gráfica y muestra el resultado de cada algoritmo paso a paso.

1. Qué vamos a simular

Para comparar algoritmos de reemplazo hay que fijar todas las condiciones menos una: la misma cadena de referencias, la misma cantidad de marcos, el mismo estado inicial (memoria vacía) y el mismo criterio (fallo o acierto en cada acceso). Lo único que cambia es la regla con la que cada algoritmo elige su víctima. Si no se cumplen esas condiciones, cualquier comparación es tramposa.

Los contendientes ya los conoces:

El simulador procesa una cadena y mide, para cada algoritmo: fallos, aciertos y tasa de fallos. Además lleva el detalle acceso por acceso, para ver no solo cuántos fallos hubo, sino dónde aparecen.

2. Los cuatro algoritmos, lado a lado

AlgoritmoVíctimaLo que necesita¿De pila?Tema
FIFOLa página más antiguaUna cola por orden de llegadaNo35
LRULa menos usada recientementeEl orden exacto de uso (lista y tabla hash) actualizado en cada accesoSí37
RelojLa primera con R = 0, dando una vuelta a las que tienen R = 1Un bit R por marco y una mano circularNo38
ÓptimoLa que se usará más tarde (o nunca)Conocer el futuro: la cadena completa por adelantadoSí36

Algoritmo de pila (stack algorithm): aquel en el que las páginas en memoria con k marcos están siempre incluidas en las que estarían con k+1. LRU y óptimo lo son; FIFO y reloj no. Por eso los de pila nunca sufren la anomalía de Belady, y los otros dos sí pueden sufrirla.

Esa propiedad es la que explica los resultados que verás en este tema: agregar un marco nunca puede perjudicar a LRU ni al óptimo, pero sí puede aumentar los fallos de FIFO y del reloj.

3. El programa de comparación

El programa siguiente implementa los cuatro algoritmos en funciones sim_* y ejecuta todas sobre la misma cadena. El número de marcos se pasa como argumento:

reemplazo.c
#include <stdio.h>
#include <stdlib.h>

#define NSEQ 12
static int seq[NSEQ] = {1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5};

/* FIFO: cola circular */
static int sim_fifo(int marcos)
{
    int mem[16], n = 0, fifo = 0, fallos = 0;
    for (int i = 0; i < NSEQ; i++) {
        int p = seq[i], found = 0;
        for (int j = 0; j < n; j++)
            if (mem[j] == p) { found = 1; break; }
        if (!found) {
            fallos++;
            if (n < marcos) mem[n++] = p;
            else { mem[fifo] = p; fifo = (fifo + 1) % marcos; }
        }
    }
    return fallos;
}

/* LRU: víctima con el último uso más antiguo */
static int sim_lru(int marcos)
{
    int mem[16], ultimo[16], n = 0, fallos = 0;
    for (int i = 0; i < NSEQ; i++) {
        int p = seq[i], found = -1;
        for (int j = 0; j < n; j++)
            if (mem[j] == p) { found = j; break; }
        if (found >= 0) {
            ultimo[found] = i;
        } else {
            fallos++;
            if (n < marcos) { mem[n] = p; ultimo[n] = i; n++; }
            else {
                int victima = 0;
                for (int j = 1; j < marcos; j++)
                    if (ultimo[j] < ultimo[victima]) victima = j;
                mem[victima] = p; ultimo[victima] = i;
            }
        }
    }
    return fallos;
}

/* Reloj: segunda oportunidad con una mano */
static int sim_reloj(int marcos)
{
    int mem[16], ref[16], n = 0, mano = 0, fallos = 0;
    for (int i = 0; i < NSEQ; i++) {
        int p = seq[i], found = -1;
        for (int j = 0; j < n; j++)
            if (mem[j] == p) { found = j; break; }
        if (found >= 0) {
            ref[found] = 1;
        } else {
            fallos++;
            if (n < marcos) { mem[n] = p; ref[n] = 1; n++; }
            else {
                while (ref[mano] == 1) { ref[mano] = 0; mano = (mano + 1) % marcos; }
                mem[mano] = p; ref[mano] = 1;
                mano = (mano + 1) % marcos;
            }
        }
    }
    return fallos;
}

/* Óptimo: víctima con el próximo uso más lejano */
static int sim_optimo(int marcos)
{
    int mem[16], n = 0, fallos = 0;
    for (int i = 0; i < NSEQ; i++) {
        int p = seq[i], found = 0;
        for (int j = 0; j < n; j++)
            if (mem[j] == p) { found = 1; break; }
        if (!found) {
            fallos++;
            if (n < marcos) mem[n++] = p;
            else {
                int victima = 0, lejano = -1;
                for (int j = 0; j < marcos; j++) {
                    int proximo = NSEQ;   /* nunca más */
                    for (int k = i + 1; k < NSEQ; k++)
                        if (seq[k] == mem[j]) { proximo = k; break; }
                    if (proximo > lejano) { lejano = proximo; victima = j; }
                }
                mem[victima] = p;
            }
        }
    }
    return fallos;
}

int main(int argc, char **argv)
{
    int marcos = argc > 1 ? atoi(argv[1]) : 3;
    printf("marcos=%d\n", marcos);
    printf("FIFO   : %d fallos de %d\n", sim_fifo(marcos), NSEQ);
    printf("LRU    : %d fallos de %d\n", sim_lru(marcos), NSEQ);
    printf("Reloj  : %d fallos de %d\n", sim_reloj(marcos), NSEQ);
    printf("Óptimo : %d fallos de %d\n", sim_optimo(marcos), NSEQ);
    return 0;
}
terminal · la misma cadena con 3 y 4 marcos
$ gcc -std=c17 -Wall -Wextra -o reemplazo reemplazo.c
$ ./reemplazo 3
marcos=3
FIFO   : 9 fallos de 12
LRU    : 10 fallos de 12
Reloj  : 9 fallos de 12
Óptimo : 7 fallos de 12
$ ./reemplazo 4
marcos=4
FIFO   : 10 fallos de 12
LRU    : 8 fallos de 12
Reloj  : 10 fallos de 12
Óptimo : 6 fallos de 12

Compara las dos ejecuciones: FIFO y reloj empeoran al pasar de 3 a 4 marcos (de 9 a 10 fallos), mientras LRU y óptimo mejoran. Es la anomalía de Belady en acción, y la verás reproducida en el simulador de la sección 5.

4. Las cadenas de prueba

No existe un algoritmo «mejor» en abstracto: el resultado depende de la cadena. El simulador incluye cuatro cadenas con propósitos distintos; esta tabla muestra lo que produce cada una con 3 marcos, para que la uses como predicción antes de ejecutar:

CadenaQué pruebaFallos (FIFO / LRU / Reloj / Óptimo)
Clásica 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5Sin localidad: cada acceso cae en una página distinta de las anteriores9 / 10 / 9 / 7
Con localidad 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3Reutiliza un conjunto pequeño: la memoria alcanza para casi todo7 / 5 / 7 / 5
Barrido 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6El conjunto de trabajo (6 páginas) no entra en 3 marcos12 / 12 / 12 / 9
Bucle pequeño 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3Todo el conjunto de trabajo entra desde el principio3 / 3 / 3 / 3

Las mismas cadenas servirán para dos lecciones: cuando el conjunto de trabajo entra en memoria, todos los algoritmos empatan tras la carga inicial; cuando no entra, ningún algoritmo en línea puede evitar los fallos, y solo el óptimo se salva porque conoce el futuro.

5. El simulador interactivo

Configura los marcos y la cadena (o elige una de las cuatro de prueba) y ejecuta: verás la tabla de fallos, la gráfica y el detalle de cada acceso para los cuatro algoritmos a la vez.

Simulador · FIFO, LRU, reloj y óptimo

TABLA DE FALLOS

GRÁFICA DE FALLOS

DETALLE POR ACCESO

6. Qué observar

7. Limitaciones del simulador

El simulador es deliberadamente simple. No modela:

8. En Linux

En un sistema real los «fallos» del simulador son los fallos de página del proceso. El sistema los cuenta y las herramientas los muestran:

terminal · fallos de página de un proceso real
$ /usr/bin/time -v ./programa 2>&1 | grep -i 'page faults'
	Minor (reclaiming a frame) page faults: 10234
	Major (requiring I/O) page faults: 0
$ perf stat -e page-faults ./programa
      1.024.318      page-faults

9. Resumen

10. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué condiciones deben fijarse para comparar dos algoritmos de reemplazo?

La misma cadena de referencias, la misma cantidad de marcos y el mismo estado inicial. Solo debe cambiar la regla de elección de víctima.

2. ¿Qué es un algoritmo de pila?

Aquel en el que las páginas en memoria con k marcos están incluidas en las de k+1 marcos. LRU y óptimo lo son.

3. ¿Qué relación hay entre los algoritmos de pila y la anomalía de Belady?

Los de pila no pueden sufrirla: agregar marcos nunca aumenta sus fallos. Los que no son de pila (FIFO y reloj) sí.

4. En la cadena clásica, ¿qué pasa al pasar de 3 a 4 marcos?

FIFO y reloj pasan de 9 a 10 fallos; LRU baja de 10 a 8 y el óptimo de 7 a 6.

5. ¿Por qué el reloj sufre la anomalía igual que FIFO?

Porque no es un algoritmo de pila: cuando todos los bits R están en 1 se comporta como FIFO y puede empeorar con más marcos.

6. ¿Qué muestra la gráfica de barras del simulador?

Los fallos de cada algoritmo como barras proporcionales al total de accesos, para comparar de un vistazo.

7. En el barrido cíclico con 3 marcos, ¿por qué todos los algoritmos en línea fallan las 12 veces?

Porque entre dos usos de una misma página se accede a otras 5 páginas y solo hay 3 marcos: la página se reemplaza antes de volver a usarse. Solo el óptimo, que conoce el futuro, logra aciertos.

8. ¿Por qué una cadena corta puede dar conclusiones engañosas?

Porque resultados como «FIFO gana a LRU» o «el reloj empata» dependen de la cadena elegida. Las conclusiones representativas surgen de trazas largas y reales.

Para practicar

  1. Ejecuta la cadena clásica con 3, 4 y 5 marcos y anota la tabla completa de fallos.
  2. Comprueba que con 3 marcos en la cadena clásica FIFO (9) gana a LRU (10) y explica por qué ocurre.
  3. Verifica con el simulador que la cadena 2, 5, 2, 4, 2, 5, 1, 1, 1, 5, 3, 5 con 3 marcos da 6 fallos en FIFO y 5 en el reloj, y explica por qué: el bit R de una página recién usada la salva del reemplazo.
  4. Modifica reemplazo.c para que la cadena se pase por línea de comandos.
  5. Genera 100 cadenas aleatorias con un script y compara los promedios; ¿en qué porcentaje gana cada algoritmo?
  6. Explica por qué el óptimo, aun siendo la cota, no puede implementarse en un sistema real.
  7. Compara las cadenas «Con localidad» y «Barrido» con los mismos marcos y relaciona la diferencia con el conjunto de trabajo (tema 33).