Tutorial complet sobre la memòria cau LRU amb implementacions
Què és la memòria cau LRU?
Els algorismes de substitució de la memòria cau estan dissenyats de manera eficient per substituir la memòria cau quan l'espai està ple. El S'ha utilitzat menys recentment (LRU) és un d'aquests algorismes. Com el seu nom indica quan la memòria cau està plena, LRU tria les dades que s'han utilitzat menys recentment i les elimina per fer espai per a les dades noves. La prioritat de les dades a la memòria cau canvia segons la necessitat d'aquestes dades, és a dir, si algunes dades s'obtenen o s'actualitzen recentment, la prioritat d'aquestes dades es canviarà i s'assignaria a la prioritat més alta, i la prioritat de les dades disminueix si resta operacions sense utilitzar després de les operacions.
Taula de contingut
- Què és la memòria cau LRU?
- Operacions a la memòria cau LRU:
- Funcionament de la memòria cau LRU:
- Formes d'implementar la memòria cau LRU:
- Implementació de la memòria cau LRU mitjançant Queue and Hashing:
- Implementació de la memòria cau LRU mitjançant la llista doblement enllaçada i el hashing:
- Implementació de la memòria cau LRU mitjançant Deque & Hashmap:
- Implementació de la memòria cau LRU mitjançant Stack & Hashmap:
- La memòria cau LRU utilitzant la implementació del comptador:
- Implementació de la memòria cau LRU mitjançant Lazy Updates:
- Anàlisi de complexitat de la memòria cau LRU:
- Avantatges de la memòria cau LRU:
- Desavantatges de la memòria cau LRU:
- Aplicació del món real de la memòria cau LRU:
LRU L'algorisme és un problema estàndard i pot tenir variacions segons les necessitats, per exemple, en els sistemes operatius LRU té un paper crucial, ja que es pot utilitzar com a algorisme de substitució de pàgines per tal de minimitzar els errors de la pàgina.
Operacions a la memòria cau LRU:
- LRUCache (capacitat c): Inicialitzeu la memòria cau LRU amb capacitat de mida positiva c.
- obtenir (clau) : retorna el valor de la clau ' k' si està present a la memòria cau en cas contrari retorna -1. També actualitza la prioritat de les dades a la memòria cau LRU.
- posar (clau, valor): Actualitzeu el valor de la clau si aquesta existeix. En cas contrari, afegiu el parell clau-valor a la memòria cau. Si el nombre de claus supera la capacitat de la memòria cau LRU, descarta la clau utilitzada menys recentment.
Funcionament de la memòria cau LRU:
Suposem que tenim una memòria cau LRU de capacitat 3 i que ens agradaria realitzar les operacions següents:
- Posa (clau=1, valor=A) a la memòria cau
- Posa (clau=2, valor=B) a la memòria cau
- Posa (clau=3, valor=C) a la memòria cau
- Obteniu (clau=2) de la memòria cau
- Obteniu (clau=4) de la memòria cau
- Posa (clau=4, valor=D) a la memòria cau
- Posa (clau=3, valor=E) a la memòria cau
- Obteniu (clau=4) de la memòria cau
- Posa (clau=1, valor=A) a la memòria cau
Les operacions anteriors es realitzen una darrere l'altra, tal com es mostra a la imatge següent:
Explicació detallada de cada operació:
- Posa (clau 1, valor A) : Com que la memòria cau LRU té capacitat buida = 3, no cal cap substitució i posem {1 : A} a la part superior, és a dir, {1 : A} té la prioritat més alta.
- Posa (clau 2, valor B) : Com que la memòria cau LRU té capacitat buida=2, de nou no cal cap substitució, però ara {2 : B} té la prioritat més alta i la prioritat de {1 : A} disminueix.
- Posa (clau 3, valor C) : Encara hi ha 1 espai buit a la memòria cau, per tant, poseu {3 : C} sense cap substitució, observeu que ara la memòria cau està plena i l'ordre actual de prioritat de més alt a més baix és {3:C}, {2:B }, {1:A}.
- Obtenir (clau 2) : Ara, retorneu el valor de clau=2 durant aquesta operació, també com que s'utilitza clau=2, ara el nou ordre de prioritat és {2:B}, {3:C}, {1:A}
- Obteniu (clau 4): Observeu que la clau 4 no està present a la memòria cau, retornem '-1' per a aquesta operació.
- Posa (clau 4, valor D) : Observeu que la memòria cau és COMPLETA, ara feu servir l'algorisme LRU per determinar quina clau s'ha utilitzat menys recentment. Com que {1:A} tenia la menor prioritat, elimineu {1:A} de la nostra memòria cau i poseu-hi {4:D}. Tingueu en compte que el nou ordre de prioritat és {4:D}, {2:B}, {3:C}
- Posa (clau 3, valor E) : Com que key=3 ja estava present a la memòria cau amb valor=C, per tant, aquesta operació no donarà lloc a l'eliminació de cap clau, sinó que actualitzarà el valor de key=3 a ' I' . Ara, el nou ordre de prioritat es convertirà en {3:E}, {4:D}, {2:B}
- Obtenir (clau 4) : retorna el valor de key=4. Ara, la nova prioritat es convertirà en {4:D}, {3:E}, {2:B}
- Posa (clau 1, valor A) : Com que la nostra memòria cau és COMPLETA, utilitzeu el nostre algorisme LRU per determinar quina clau s'ha utilitzat menys recentment, i com que {2:B} tenia la menor prioritat, elimineu {2:B} de la nostra memòria cau i introduïu {1:A} a la memòria cau. Ara, el nou ordre de prioritat és {1:A}, {4:D}, {3:E}
Formes d'implementar la memòria cau LRU:
La memòria cau LRU es pot implementar de diverses maneres i cada programador pot triar un enfocament diferent. Tanmateix, a continuació es mostren els enfocaments d'ús habitual:
- LRU utilitzant cua i hashing
- LRU utilitzant Llista doblement enllaçada + hashing
- LRU utilitzant Deque
- LRU utilitzant Stack
- LRU utilitzant Implementació del comptador
- LRU utilitzant Lazy Updates
Implementació de la memòria cau LRU mitjançant Queue and Hashing:
Utilitzem dues estructures de dades per implementar una memòria cau LRU.
- Cua s'implementa mitjançant una llista doblement enllaçada. La mida màxima de la cua serà igual al nombre total de fotogrames disponibles (mida de la memòria cau). Les pàgines utilitzades més recentment estaran prop de la part frontal i les pàgines menys utilitzades estaran prop de la part posterior.
- Un Hash amb el número de pàgina com a clau i l'adreça del node de cua corresponent com a valor.
Quan es fa referència a una pàgina, és possible que la pàgina requerida estigui a la memòria. Si està a la memòria, hem de desconnectar el node de la llista i portar-lo al capdavant de la cua.
Si la pàgina requerida no està a la memòria, la portem a la memòria. En paraules senzilles, afegim un nou node al capdavant de la cua i actualitzem l'adreça del node corresponent al hash. Si la cua està plena, és a dir, tots els fotogrames estan plens, eliminem un node de la part posterior de la cua i afegim el nou node al davant de la cua.
Il·lustració:
Considerem les operacions, Es refereix clau x amb a la memòria cau de LRU: { 1, 2, 3, 4, 1, 2, 5, 1, 2, 3 }
Nota: Inicialment no hi ha cap pàgina a la memòria.Les imatges següents mostren l'execució pas a pas de les operacions anteriors a la memòria cau LRU.
![]()
Algorisme:
- Creeu una classe LRUCache amb declarar una llista de tipus int, un mapa de tipus no ordenat
, i una variable per emmagatzemar la mida màxima de la memòria cau - A la funció de referència de LRUCache
- Si aquest valor no està present a la cua, premeu aquest valor davant de la cua i elimineu l'últim valor si la cua està plena
- Si el valor ja està present, elimineu-lo de la cua i premeu-lo al davant de la cua
- A la funció de visualització d'impressió, el LRUCache utilitza la cua començant per la part frontal
A continuació es mostra la implementació de l'enfocament anterior:
C++
// We can use stl container list as a double> // ended queue to store the cache keys, with> // the descending time of reference from front> // to back and a set container to check presence> // of a key. But to fetch the address of the key> // in the list using find(), it takes O(N) time.> // This can be optimized by storing a reference> // (iterator) to each key in a hash map.> #include> using> namespace> std;> > class> LRUCache {> > // store keys of cache> > list <> int> >dq;>>> int> csize;> // maximum capacity of cache> > public> :> > LRUCache(> int> );> > void> refer(> int> );> > void> display();> };> > // Declare the size> LRUCache::LRUCache(> int> n) { csize = n; }> > // Refers key x with in the LRU cache> void> LRUCache::refer(> int> x)> {> > // not present in cache> > if> (ma.find(x) == ma.end()) {> > // cache is full> > if> (dq.size() == csize) {> > // delete least recently used element> > int> last = dq.back();> > > // Pops the last element> > dq.pop_back();> > > // Erase the last> > ma.erase(last);> > }> > }> > > // present in cache> > else> > dq.erase(ma[x]);> > > // update reference> > dq.push_front(x);> > ma[x] = dq.begin();> }> > // Function to display contents of cache> void> LRUCache::display()> {> > > // Iterate in the deque and print> > // all the elements in it> > for> (> auto> it = dq.begin(); it != dq.end(); it++)> > cout < < (*it) < <> ' '> ;> > > cout < < endl;> }> > // Driver Code> int> main()> {> > LRUCache ca(4);> > > ca.refer(1);> > ca.refer(2);> > ca.refer(3);> > ca.refer(1);> > ca.refer(4);> > ca.refer(5);> > ca.display();> > > return> 0;> }> // This code is contributed by Satish Srinivas> |
C
// A C program to show implementation of LRU cache> #include> #include> > // A Queue Node (Queue is implemented using Doubly Linked> // List)> typedef> struct> QNode {> > struct> QNode *prev, *next;> > unsigned> > pageNumber;> // the page number stored in this QNode> } QNode;> > // A Queue (A FIFO collection of Queue Nodes)> typedef> struct> Queue {> > unsigned count;> // Number of filled frames> > unsigned numberOfFrames;> // total number of frames> > QNode *front, *rear;> } Queue;> > // A hash (Collection of pointers to Queue Nodes)> typedef> struct> Hash {> > int> capacity;> // how many pages can be there> > QNode** array;> // an array of queue nodes> } Hash;> > // A utility function to create a new Queue Node. The queue> // Node will store the given 'pageNumber'> QNode* newQNode(unsigned pageNumber)> {> > // Allocate memory and assign 'pageNumber'> > QNode* temp = (QNode*)> malloc> (> sizeof> (QNode));> > temp->pageNumber = pageNumber;>>> > return> temp;> }> > // A utility function to create an empty Queue.> // The queue can have at most 'numberOfFrames' nodes> Queue* createQueue(> int> numberOfFrames)> {> > Queue* queue = (Queue*)> malloc> (> sizeof> (Queue));> > > // The queue is empty> > queue->recompte = 0;>>> > // Number of frames that can be stored in memory> > queue->numberOfFrames = nombreOfFrames;>>> > // Create an array of pointers for referring queue nodes> > hash->matriu>>> (QNode*));> > > // Initialize all hash entries as empty> > int> i;> > for> (i = 0; i capacity; ++i)> > hash->matriu[i] = NULL;>>> > // A utility function to check if queue is empty> int> isQueueEmpty(Queue* queue)> {> > return> queue->posterior == NULL;>>> queue->davant = NULL;>>> queue->posterior = cua->darrera->anterior;>>> queue->darrere->següent = NULL;>>> > // A function to add a page with given 'pageNumber' to both> // queue and hash> void> Enqueue(Queue* queue, Hash* hash, unsigned pageNumber)> {> > // If all frames are full, remove the page at the rear> > if> (AreAllFramesFull(queue)) {> > // remove page from hash> > hash->array[queue->rear->pageNumber] = NULL;>>> > // If queue is empty, change both front and rear> > // pointers> > if> (isQueueEmpty(queue))> > queue->posterior = cua->front = temp;>>> queue->davant = temp;>>> > // increment number of full frames> > queue->comptar++;>>> // are two cases:> // 1. Frame is not there in memory, we bring it in memory> // and add to the front of queue> // 2. Frame is there in memory, we move the frame to front> // of queue> void> ReferencePage(Queue* queue, Hash* hash,> > unsigned pageNumber)> {> > QNode* reqPage = hash->matriu[número de pàgina];>>> // Unlink rquested page from its current location> > // in queue.> > reqPage->prev->next = reqPage->next;>>> reqPage->següent->anterior = reqPage->anterior;>>> queue->darrere->següent = NULL;>>> reqPage->anterior = NULL;>>> > // Change front to the requested page> > queue->front = reqPage;>>> , q->portada->número de pàgina);>>> , q->front->next->pageNumber);>>> , q->front->next->next->pageNumber);>>> , q->front->next->next->next->pageNumber);>>> |