Algoritmo de eliminación de reloj redis (11) escrito a mano en Java detallado e implementación desde cero

Prefacio

Java implementa redis a mano desde cero (1) ¿Cómo lograr un caché de tamaño fijo?

Java implementa redis a mano desde cero (tres) principio de expiración de redis

Java implementa redis a mano desde cero (3) ¿Cómo reiniciar sin perder datos de memoria?

Java implementa redis desde cero a mano (cuatro) agregar oyente

Otra forma de implementar la estrategia de vencimiento de redis (5) desde cero

Java implementa redis a mano desde cero (6) Principio de persistencia AOF detallado e implementación

Redis manuscrita de Java desde cero (7) Explicación detallada de la estrategia de eliminación de caché LRU

Anteriormente, implementamos estrategias de eliminación comunes como FIFO / LRU / LFU, pero en el sistema operativo, en realidad se usa el algoritmo de reemplazo de la página del reloj.

El rendimiento de LRU es realmente bueno, pero consume más memoria y es más complicado de implementar.

El algoritmo de reemplazo de la página del reloj es una implementación de algoritmo similar a LRU, que puede verse como una mejora del algoritmo FIFO.

Algoritmo de reemplazo de página de reloj

¿Por qué se necesita el algoritmo del reloj?

El rendimiento del algoritmo LRU es cercano al del OPT, pero es difícil de implementar y tiene una gran sobrecarga; el algoritmo FIFO es simple de implementar, pero tiene un rendimiento deficiente.

Por lo tanto, los diseñadores del sistema operativo han probado muchos algoritmos, tratando de acercarse al rendimiento de LRU con una sobrecarga relativamente pequeña. Dichos algoritmos son todas variantes del algoritmo CLOCK.

Dado que este algoritmo verifica la condición de cada página de forma cíclica, se denomina algoritmo CLOCK, también conocido como algoritmo No utilizado recientemente (NRU).

La idea basica

Se necesita el bit de acceso de la entrada de la tabla de páginas. Cuando se carga una página en la memoria, el bit se inicializa a 0, y luego, si se accede a la página (lectura / escritura), el hardware lo establece en 1.

Organice cada página en una lista enlazada circular (similar a la superficie de un reloj) y apunte el puntero a la página más antigua (la más avanzada);

Cuando se produce una interrupción por fallo de página, se examina la página más antigua a la que apunta el puntero, y si su acceso es 0, se elimina inmediatamente. Si el acceso es 1, la posición se establece en 0 y luego el puntero se mueve hacia abajo un espacio. Haga esto hasta que encuentre la página eliminada y luego mueva el puntero a la siguiente cuadrícula.

Dudas personales

(1) ¿Qué pasa si los elementos son todos 1 después de buscar un círculo?

¿Es correcto tomar el primer elemento directamente por defecto? Esto se considera un retorno al mecanismo FIFO simple.

(2) Problemas de rendimiento de acceso

El recorrido aquí se puede considerar como una lista enlazada circular:

Contenido de cada nodo:

K key;
boolean accessFlag;

El FIFO ingenuo es muy simple, simplemente arroje elementos directamente a la cola y luego elimine el elemento más antiguo.

Si la lista enlazada se usa realmente como estructura de datos, la complejidad del tiempo de búsqueda y actualización es O (N), obviamente el rendimiento es promedio.

La solución concebible es almacenar clave + nodos de lista doblemente enlazados en HashMap.

En comparación con la versión de LRU con rendimiento mejorado, cada actualización no realiza ajustes de eliminación de nodos, sino que solo actualiza los bits de marca correspondientes.

Algoritmo CLOCK simple

Es mediante la asociación de un bit adicional (bit de referencia) a cada página visitada, que también se denomina bit de uso en algunos lugares.

Su idea principal es: cuando se carga una página en la memoria principal, el bit de uso se inicializa a 0; si se accede a la página más tarde, el bit de uso sigue marcado como 1.

Para el algoritmo de reemplazo de página, el conjunto de tramas candidato se puede considerar como un búfer circular y hay un puntero asociado con el búfer. Cuando se encuentra un reemplazo de página, el puntero apunta al siguiente fotograma en el búfer.

Si esta página ingresa a la memoria principal y se encuentra que no hay un marco libre (marco), es decir, los bits de uso de todas las páginas son 1, entonces un búfer se enlaza desde el puntero en este momento, y los bits de uso anteriores se borran a 0 y se dejan al principio. En la posición, cambie la página correspondiente al marco.

ps: Si no se encuentra ninguna trama libre aquí, se borrarán todos los bits utilizados.

ejemplo

Tome el siguiente proceso de reemplazo de página como ejemplo, las páginas visitadas son: 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5.

Hay 4 cuadros libres en la memoria principal y la estructura correspondiente de cada página es (número de página, bit de uso).

La primera página número 1 ingresa a la memoria principal, hay marcos inactivos en la memoria principal y su bit de uso se marca como 1. Dado que no hay una página 1 en la memoria principal antes, se producirá una interrupción por falla de página.

De la misma manera, las siguientes páginas 2, 3 y 4 ingresan a la memoria principal, y sus bits de uso se registran como 1, y se produce una interrupción de falla de página.

Cuando las páginas siguientes 1, 2 ingresan a la memoria principal, dado que las páginas 1 y 2 ya están en la memoria principal, no se procesan.

Cuando la siguiente página 5 ingresa a la memoria principal, no hay ningún marco libre en la memoria principal. En este momento, todo el búfer se mueve circularmente con el puntero, y todos los bits usados ​​de la página anterior se borran a 0, es decir, páginas 1, 2, 3, 4 Los bits usados ​​correspondientes son todos 0, el puntero vuelve a la posición original, la página 1 se reemplaza, la página 5 se cambia a la memoria principal y los bits usados ​​se marcan como 1.

Por analogía, se puede ver que CLOCK tiene interrupciones de falla de 10 páginas.

Ingrese la descripción de la imagen

Gclock (algoritmo de reemplazo de página de reloj generalizado)

Idea de algoritmo

Este algoritmo es una variante de Clock.

En comparación con el bit de la bandera del reloj que usa 0 y 1 binarios, el bit de la bandera Gclock usa un número entero, lo que significa que teóricamente se puede incrementar hasta el infinito.

principio de funcionamiento

(1) Cuando el objeto que se va a almacenar en caché está en la caché, agregue 1 al valor de su bit de bandera. Al mismo tiempo, el puntero apunta al siguiente objeto del objeto.

(2) Si no está en la caché, verifique el bit de marca del objeto al que apunta el puntero. Si es 0, reemplace el objeto con el objeto que se almacenará en caché; de lo contrario, reduzca el valor del bit de bandera en 1 y el puntero apuntará al siguiente objeto. Y así sucesivamente hasta eliminar un objeto. Dado que se permite que el valor del bit de bandera sea mayor que 1, el puntero puede realizar un bucle varias veces para eliminar un objeto.

ps: Esto es un poco similar a la versión simplificada de LFU, que cuenta las ocurrencias correspondientes.

WSclock (algoritmo de reemplazo de página de reloj de configuración de trabajo)

Idea de algoritmo

Este algoritmo también es una variante del reloj y puede ser el algoritmo más utilizado en la práctica.

Utiliza el principio del reloj y es una versión mejorada del algoritmo ws.

La estructura de datos del algoritmo es una lista enlazada circular.Cada objeto de caché guarda el "tiempo usado recientemente" rt y la bandera R de "si hacer referencia", y usa un temporizador periódico t. la edad se expresa como la diferencia entre la hora actual y la rt

principio de funcionamiento

(1) Cuando el objeto que se va a almacenar en caché está en la caché, actualice rt a la hora actual. Al mismo tiempo, el puntero apunta al siguiente objeto del objeto.

(2) Si no existe en el caché, si el caché no está lleno, rt en la ubicación apuntada por el puntero de actualización es la hora actual y R es 1. Al mismo tiempo, el puntero apunta al siguiente objeto. Si está lleno, es necesario eliminar un objeto. Compruebe el objeto al que apunta el puntero,

  • R es 1, lo que indica que el objeto está en el conjunto de trabajo, restablece R a 0 y el puntero apunta al siguiente objeto.

  • R es 0. Si la edad es mayor que t, lo que indica que el objeto no está en el conjunto de trabajo, reemplace el objeto y establezca R en 1 y rt en la hora actual. Si la edad no es mayor que t, continúe buscando objetos eliminados. Si regresa a la posición donde comenzó el puntero y no se ha encontrado el objeto eliminado, se elimina el primer objeto con R 0.

Método de segunda oportunidad (o reloj mejorado)

Algoritmo CLOCK mejorado

Idea: reducir la sobrecarga de procesamiento de errores de página al modificar páginas

Modifique el algoritmo Clock para permitir que las páginas sucias se retengan siempre en un escaneo del cabezal del reloj, mientras se usa el bit sucio (también llamado bit de escritura) y el bit de uso para guiar el reemplazo

Flujo del algoritmo

En el algoritmo CLOCK anterior, además del bit utilizado, se agregó un bit modificado, que también se denomina bit sucio en algunos lugares.

Ahora cada página tiene dos estados, a saber (usar bit, modificar bit), que se pueden dividir en los siguientes cuatro casos:

(0,0): No usado o modificado recientemente, ¡la mejor condición!

(0,1): Modificado pero no utilizado recientemente, se escribirá

(1,0): Usado pero no modificado, se volverá a usar en la siguiente ronda.

(1,1): Usado y modificado, la última selección de la siguiente ronda de reemplazo de página

ejemplo

Tome el siguiente proceso de reemplazo de página como ejemplo:

Las páginas visitadas son: 0,1,3,6,2,4,5,2,5,0,3,1,2,5,4,1,0. El número rojo indica la página a modificar, es decir Su bit modificado se establecerá en 1. En la figura siguiente, estas páginas se indican en cursiva, y los bits usados ​​y los bits modificados se muestran en la figura siguiente. El siguiente "¿Fallo?" Significa el número de veces que se encuentran marcos libres cuando una página es defectuosa.

Ingrese la descripción de la imagen

Orden de reemplazo

  1. Comience desde la posición actual del puntero para encontrar la página en la memoria principal que satisfaga (use bit, modifique bit) como (0,0);

  2. Si la condición no se encuentra en el paso 1, busque la página cuyo estado es (0,1);

  3. Si aún no se encuentra, el puntero vuelve a la posición original y los bits de uso de todas las páginas del conjunto se establecen en 0. Repita el paso 1 y, si es necesario, repita el paso 2 para que pueda encontrar la página que desea reemplazar.

Java implementa el algoritmo de reloj

Descripción

Este artículo implementa principalmente una versión simple del algoritmo de reloj y agrega ciertas optimizaciones de rendimiento a la implementación convencional. (Toda la red puede ser exclusiva, o la primera en lograrlo)

La optimización se basa principalmente en consideraciones de rendimiento, similar a la optimización de rendimiento anterior para LRU, que optimiza las operaciones de consulta de O (N) a O (1).

Idea de realización

Definimos una lista enlazada circular que cumple con el escenario comercial actual (esto más adelante también puede ser independiente, y hay tiempo para escribir un proyecto de estructura de datos separado para una fácil reutilización)

Defina el nodo que contiene accessFlag.

Usamos una lista doblemente enlazada en lugar de una lista enlazada individualmente, por lo que eliminar el rendimiento es el mejor.

Utilice el mapa para guardar la información de la clave, evite recorrer toda la lista vinculada para determinar si la clave existe e intercambie espacio por tiempo.

Bueno, el siguiente paso es la etapa de codificación feliz.

Código

Definición de nodo

/**
 * 循环链表节点
 * @author binbin.hou
 * @since 0.0.15
 * @param <K> key
 * @param <V> value
 */
public class CircleListNode<K,V> {

    /**
     * 键
     * @since 0.0.15
     */
    private K key;

    /**
     * 值
     * @since 0.0.15
     */
    private V value = null;

    /**
     * 是否被访问过
     * @since 0.0.15
     */
    private boolean accessFlag = false;

    /**
     * 后一个节点
     * @since 0.0.15
     */
    private CircleListNode<K, V> pre;

    /**
     * 后一个节点
     * @since 0.0.15
     */
    private CircleListNode<K, V> next;

    //getter & setter
}

Aquí hay algunos elementos simples: clave, valor, accessFlag (identificación de si ha accedido a él), y luego, pre-usuarios para lograr una lista doblemente enlazada.

Implementación de lista doblemente enlazada

Atributos basicos

Para ser coherentes con la lista original de Lru doblemente enlazada, implementamos la interfaz original.

public class LruMapCircleList<K,V> implements ILruMap<K,V> {

    private static final Log log = LogFactory.getLog(LruMapCircleList.class);

    /**
     * 头结点
     * @since 0.0.15
     */
    private CircleListNode<K,V> head;

    /**
     * 映射 map
     * @since 0.0.15
     */
    private Map<K, CircleListNode<K,V>> indexMap;

    public LruMapCircleList() {
        // 双向循环链表
        this.head = new CircleListNode<>(null);
        this.head.next(this.head);
        this.head.pre(this.head);

        indexMap = new HashMap<>();
    }

}

Inicialice el nodo Head y el usuario indexMap guarda la relación entre la clave y el nodo bidireccional.

Eliminar elemento

/**
 * 移除元素
 *
 * 1. 是否存在,不存在则忽略
 * 2. 存在则移除,从链表+map中移除
 *
 * head==>1==>2==>head
 *
 * 删除 2 之后:
 * head==>1==>head
 * @param key 元素
 * @since 0.0.15
 */
@Override
public void removeKey(final K key) {
    CircleListNode<K,V> node = indexMap.get(key);
    if(ObjectUtil.isNull(node)) {
        log.warn("对应的删除信息不存在:{}", key);
        return;
    }
    CircleListNode<K,V> pre = node.pre();
    CircleListNode<K,V> next = node.next();
    //1-->(x2)-->3  直接移除2
    pre.next(next);
    next.pre(pre);
    indexMap.remove(key);
    log.debug("Key: {} 从循环链表中移除", key);
}

No es difícil eliminar nodos, simplemente elimine los nodos de la lista circular enlazada y elimine la información en el indexMap.

Actualizar

Se usa el mismo método para poner / obtener aquí. De hecho, si desea implementar una versión mejorada del algoritmo de reloj, es mejor distinguir entre los dos, pero siento que el principio es similar, por lo que no lo implementaré aquí más. Se estima que esto se elimina La última sección del algoritmo.

/**
 * 放入元素
 *
 * 类似于 FIFO,直接放在队列的最后
 * 
 * head==>1==>head
 * 加入元素:
 *
 * head==>1==>2==>head
 *
 * (1)如果元素不存在,则直接插入。
 * 默认 accessFlag = 0;
 * (2)如果已经存在,则更新 accessFlag=1;
 *
 * @param key 元素
 * @since 0.0.15
 */
@Override
public void updateKey(final K key) {
    CircleListNode<K,V> node = indexMap.get(key);
    // 存在
    if(ObjectUtil.isNotNull(node)) {
        node.accessFlag(true);
        log.debug("节点已存在,设置节点访问标识为 true, key: {}", key);
    } else {
        // 不存在,则插入到最后
        node = new CircleListNode<>(key);
        CircleListNode<K,V> tail = head.pre();
        tail.next(node);
        node.pre(tail);
        node.next(head);
        head.pre(node);
        // 放入 indexMap 中,便于快速定位
        indexMap.put(key, node);
        log.debug("节点不存在,新增节点到链表中:{}", key);
    }
}

El propósito principal aquí es distinguir si el siguiente nodo ya existe.

(1) Existente, obtenga el nodo directamente, actualice accessFlag = true;

(2) No existe: inserte un nuevo nodo, accessFlag = false

Datos obsoletos

/**
 * 删除最老的元素
 *
 * (1)从 head.next 开始遍历,如果元素 accessFlag = 0,则直接移除
 * (2)如果 accessFlag=1,则设置其值为0,循环下一个节点。
 *
 * @return 结果
 * @since 0.0.15
 */
@Override
public ICacheEntry<K, V> removeEldest() {
    //fast-fail
    if(isEmpty()) {
        log.error("当前列表为空,无法进行删除");
        throw new CacheRuntimeException("不可删除头结点!");
    }
    // 从最老的元素开始,此处直接从 head.next 开始,后续可以考虑优化记录这个 key
    CircleListNode<K,V> node = this.head;
    while (node.next() != this.head) {
        // 下一个元素
        node = node.next();
        if(!node.accessFlag()) {
            // 未访问,直接淘汰
            K key = node.key();
            this.removeKey(key);
            return CacheEntry.of(key, node.value());
        } else {
            // 设置当前 accessFlag = 0,继续下一个
            node.accessFlag(false);
        }
    }
    // 如果循环一遍都没找到,直接取第一个元素即可。
    CircleListNode<K,V> firstNode = this.head.next();
    return CacheEntry.of(firstNode.key(), firstNode.value());
}

Atraviese el nodo directamente y elimínelo cuando accessFlag = 0.

Si accessFlag = 1, establezca su valor en 0 y luego continúe con el siguiente. (Aquí hay una sensación de que la medalla de oro solo se puede usar una vez)

No se encontró después del bucle. De hecho, solo busque head.next directamente y baje a FIFO. Por supuesto, debido a que hemos actualizado accessFlag = 0, podemos continuar el ciclo.

  • Deficiencias de implementación

Hay un punto para mejorar: no necesariamente iniciamos el ciclo todo el tiempo. De hecho, las deficiencias son más obvias, por lo que el elemento que ingresa primero a la cola debe ser eliminado por segunda vez y siempre pueden existir otros elementos no visitados, se puede utilizar un elemento para recordar esta posición. (El siguiente nodo del nodo que se eliminó la última vez), creo que esto está más en línea con la idea del algoritmo del reloj.

Otro método es no establecer el accessFlag al que se accede en 0, y el elemento no se puede encontrar en un bucle y degradarlo directamente a FIFO. Sin embargo, después de acceder a la mayoría de los elementos, el rendimiento se deteriorará. Por eso se recomienda marcar la posición del último ciclo.

transferir

Cuando el caché está lleno, podemos llamar a la lista enlazada circular actual:

import com.github.houbb.cache.api.ICache;
import com.github.houbb.cache.api.ICacheEntry;
import com.github.houbb.cache.api.ICacheEvictContext;
import com.github.houbb.cache.core.model.CacheEntry;
import com.github.houbb.cache.core.support.struct.lru.ILruMap;
import com.github.houbb.cache.core.support.struct.lru.impl.LruMapCircleList;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

/**
 * 淘汰策略-clock 算法
 *
 * @author binbin.hou
 * @since 0.0.15
 */
public class CacheEvictClock<K,V> extends AbstractCacheEvict<K,V> {

    private static final Log log = LogFactory.getLog(CacheEvictClock.class);

    /**
     * 循环链表
     * @since 0.0.15
     */
    private final ILruMap<K,V> circleList;

    public CacheEvictClock() {
        this.circleList = new LruMapCircleList<>();
    }

    @Override
    protected ICacheEntry<K, V> doEvict(ICacheEvictContext<K, V> context) {
        ICacheEntry<K, V> result = null;
        final ICache<K,V> cache = context.cache();
        // 超过限制,移除队尾的元素
        if(cache.size() >= context.size()) {
            ICacheEntry<K,V>  evictEntry = circleList.removeEldest();;
            // 执行缓存移除操作
            final K evictKey = evictEntry.key();
            V evictValue = cache.remove(evictKey);

            log.debug("基于 clock 算法淘汰 key:{}, value: {}", evictKey, evictValue);
            result = new CacheEntry<>(evictKey, evictValue);
        }

        return result;
    }

    /**
     * 更新信息
     * @param key 元素
     * @since 0.0.15
     */
    @Override
    public void updateKey(final K key) {
        this.circleList.updateKey(key);
    }

    /**
     * 移除元素
     *
     * @param key 元素
     * @since 0.0.15
     */
    @Override
    public void removeKey(final K key) {
        this.circleList.removeKey(key);
    }

}

De hecho, no hay ninguna dificultad para llamar al lugar, simplemente llame al método directamente.

prueba

Bien, verifiquémoslo brevemente después de escribir el código.

Código de prueba

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .size(3)
        .evict(CacheEvicts.<String, String>clock())
        .build();
cache.put("A", "hello");
cache.put("B", "world");
cache.put("C", "FIFO");
// 访问一次A
cache.get("A");
cache.put("D", "LRU");
Assert.assertEquals(3, cache.size());
System.out.println(cache.keySet());

Iniciar sesión

[DEBUG] [2020-10-07 11:32:55.396] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.updateKey] - 节点不存在,新增节点到链表中:A
[DEBUG] [2020-10-07 11:32:55.398] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.updateKey] - 节点不存在,新增节点到链表中:B
[DEBUG] [2020-10-07 11:32:55.401] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.updateKey] - 节点不存在,新增节点到链表中:C
[DEBUG] [2020-10-07 11:32:55.403] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.updateKey] - 节点已存在,设置节点访问标识为 true, key: A
[DEBUG] [2020-10-07 11:32:55.404] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.removeKey] - Key: B 从循环链表中移除
[DEBUG] [2020-10-07 11:32:55.406] [main] [c.g.h.c.c.s.e.CacheEvictClock.doEvict] - 基于 clock 算法淘汰 key:B, value: world
[DEBUG] [2020-10-07 11:32:55.410] [main] [c.g.h.c.c.s.l.r.CacheRemoveListener.listen] - Remove key: B, value: world, type: evict
[DEBUG] [2020-10-07 11:32:55.411] [main] [c.g.h.c.c.s.s.l.i.LruMapCircleList.updateKey] - 节点不存在,新增节点到链表中:D
[D, A, C]

Cumplir con nuestras expectativas.

Comparación de LRU, FIFO y Clock

LRU y FIFO son esencialmente ideas de primero en entrar, primero en salir, pero LRU se basa en el tiempo de acceso más reciente de las páginas para ordenar, por lo que es necesario ajustar dinámicamente el orden de cada página durante cada acceso a la página (la más reciente de cada página) El tiempo de acceso ha cambiado), mientras que el FIFO ordena el tiempo en que la página ingresa a la memoria, este tiempo es fijo, por lo que la secuencia de páginas es fija.

Si el programa es local, LRU será bueno. Si no se ha accedido a todas las páginas de la memoria, estas degenerarán en FIFO (por ejemplo, no se ha accedido a la página después de ingresar a la memoria, y la hora del último acceso es la misma que la hora en que se ingresó la memoria).

El rendimiento del algoritmo LRU es mejor, pero la sobrecarga del sistema es relativamente grande; la sobrecarga del sistema del algoritmo FIFO es relativamente pequeña, pero puede ocurrir el fenómeno Belady.

Por lo tanto, la mejor opción es el algoritmo Reloj. No necesita ajustar dinámicamente el orden de las páginas en la lista vinculada cada vez que se accede a una página. En su lugar, solo hace una marca y espera a que ocurra un error de página antes de moverla a la lista vinculada. Al final.

Para las páginas a las que no se accede en la memoria, el algoritmo Clock funciona tan bien como LRU, pero para aquellas páginas a las que se ha accedido, no puede recordar su secuencia de acceso exacta como LRU.

Suplemento de algoritmo de reemplazo

Básicamente, hemos cubierto los algoritmos de reemplazo comunes.

Sin embargo, hay muchas variantes de algoritmos, y hay muchos algoritmos en diferentes escenarios, aquí se agrega el algoritmo que no se explica en detalle, y aquí no se hará la implementación correspondiente.

El propósito es mejorar el sistema cognitivo de todo el algoritmo de eliminación.

Algoritmo de permutación óptima (OPT)

Las páginas eliminadas seleccionadas por el algoritmo de reemplazo Óptimo (OPT) nunca se utilizarán en el futuro, o las páginas a las que no se accederá dentro del período de tiempo más largo, de modo que se pueda garantizar la tasa de falla de página más baja.

Sin embargo, debido a que las personas no pueden predecir cuál de las miles de páginas en la memoria del proceso no se accederá durante más tiempo en el futuro, este algoritmo no se puede implementar.

El mejor algoritmo de reemplazo se puede utilizar para evaluar otros algoritmos. Suponga que el sistema asigna tres bloques físicos a un proceso y considere la siguiente cadena de referencia del número de página:

7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1

Cuando el proceso se está ejecutando, las tres páginas 7, 0 y 1 se cargan secuencialmente en la memoria.

Cuando un proceso quiere acceder a la página 2, se genera una interrupción por fallo de página, de acuerdo con el mejor algoritmo de reemplazo, se selecciona y elimina la página 7 que necesita ser transferida para el acceso 18.

Entonces, cuando se accede a la página 0, no hay necesidad de generar una interrupción por falla de página porque ya está en la memoria. Cuando se accede a la página 3, la página 1 se eliminará de acuerdo con el mejor algoritmo de reemplazo ... y así sucesivamente, como se muestra en la Figura 3-26.

Se puede ver en la figura que se utiliza el mejor algoritmo de reemplazo.

Se puede ver que el número de interrupciones por fallas de página es 9 y el número de reemplazos de página es 6.

Ingrese la descripción de la imagen

Por supuesto, este es un algoritmo teórico, que en realidad es imposible de lograr, porque no podemos predecir cómo se utilizarán los datos posteriores.

Algoritmo de búfer de página (PBA: Algoritmo de búfer de página) 

Aunque los algoritmos de reemplazo de reloj y LRU son mejores que los algoritmos FIFO, ambos requieren cierto soporte de hardware y requieren más gastos generales Además, el reemplazo de una página modificada es más costoso que el reemplazo de una página no modificada.

El algoritmo de búfer de página (PBA) no solo puede mejorar el rendimiento del sistema de búsqueda, sino que también puede adoptar una estrategia de reemplazo más simple.

El sistema operativo VAX / VMS utiliza el algoritmo de búfer de página. Utiliza los métodos de sustitución local y asignación de variables antes mencionados, y el algoritmo de sustitución utiliza FIFO.

El algoritmo estipula que una página eliminada se coloca en una de las dos listas enlazadas, es decir, si la página no ha sido modificada, se coloca directamente en la lista enlazada libre; de ​​lo contrario, se coloca en la lista enlazada de páginas modificadas. Cabe señalar que la página no se mueve físicamente en la memoria en este momento, sino que solo la entrada en la tabla de páginas se mueve a una de las dos listas vinculadas.

La lista de páginas enlazadas libres es en realidad una lista enlazada de bloques físicos libres.Cada bloque físico es gratis, por lo que se pueden cargar programas o datos. Cuando es necesario leer una página, se puede utilizar el primer bloque físico de la lista vinculada de bloques físicos libres para cargar la página. Cuando hay una página sin modificar para intercambiar, en realidad no se cambia de memoria, pero el bloque físico donde se encuentra la página sin modificar se cuelga al final de la lista de páginas enlazadas libres.

Del mismo modo, al reemplazar una página modificada, el bloque físico en el que se encuentra también se cuelga al final de la lista vinculada a la página modificada. De esta forma, las páginas que se han modificado y las páginas que no se han modificado aún se conservan en la memoria. Cuando el proceso acceda a estas páginas nuevamente en el futuro, solo se necesita una pequeña cantidad de gastos generales para que estas páginas regresen al conjunto residente del proceso. Cuando el número de páginas modificadas alcanza un cierto valor, por ejemplo, 64 páginas, se vuelven a escribir en el disco juntas, lo que reduce significativamente el número de operaciones de E / S del disco.

Se ha implementado un algoritmo de almacenamiento en búfer de páginas más simple en el sistema operativo MACH, pero no distingue entre páginas modificadas y páginas no modificadas.

Comparación de algoritmos de reemplazo

algoritmo Comentario
Algoritmo óptimo No alcanzable, pero se puede utilizar como punto de referencia
Algoritmo NRU (no utilizado recientemente) Aproximación aproximada de LRU
Algoritmo FIFO Puede descartar páginas importantes (de uso frecuente)
Algoritmo de segunda oportunidad Gran mejora sobre FIFO
Algoritmo de reloj realista
Algoritmo LRU (menos utilizado recientemente) Muy bien, pero difícil de lograr
Algoritmo NFU (menos utilizado) Aproximación de LRU
Algoritmo de envejecimiento Muy similar a LRU
Algoritmo de conjunto de trabajo Caro de implementar
Algoritmo de reloj de conjunto de trabajo Buen algoritmo eficaz

resumen

El algoritmo de reloj es una compensación En aplicaciones prácticas reales, el sistema operativo elige este algoritmo.

La ventaja de la comprensión personal del reloj es que no necesita actualizar la posición del elemento cada vez que lo visita con frecuencia , solo necesita actualizar una vez cuando se elimina. Aunque usamos la optimización de lista doblemente vinculada en LRU, la complejidad del tiempo es O (1), pero Es un desperdicio.

El algoritmo de eliminación de caché básicamente ha llegado a su fin aquí. Gracias por su apoyo y espero que pueda obtener algo.

Dirección de fuente abierta:https://github.com/houbb/cache

Si cree que este artículo es útil para usted, puede dar me gusta, comentar, marcar y seguir una ola. Tu aliento es mi mayor motivación ~

No sé lo que has ganado O si tiene más ideas, bienvenido a discutir conmigo en el área de mensajes y esperamos conocer sus pensamientos.

Aprendizaje profundo

Supongo que te gusta

Origin blog.51cto.com/9250070/2540345
Recomendado
Clasificación