Täydellinen opetusohjelma LRU-välimuistista ja toteutuksista
Mikä on LRU-välimuisti?
Välimuistin korvausalgoritmit on suunniteltu tehokkaasti korvaamaan välimuisti, kun tila on täynnä. The Vähiten käytetty (LRU) on yksi näistä algoritmeista. Kuten nimestä voi päätellä, kun välimuisti on täynnä, LRU poimii vähiten käytetyt tiedot ja poistaa ne tehdäkseen tilaa uusille tiedoille. Välimuistissa olevien tietojen prioriteetti muuttuu näiden tietojen tarpeen mukaan, eli jos jotakin dataa noudetaan tai päivitetään äskettäin, näiden tietojen prioriteetti muutetaan ja asetetaan korkeimpaan prioriteettiin, ja datan prioriteetti laskee, jos se jää käyttämättä toimintojen jälkeen.
Sisällysluettelo
- Mikä on LRU-välimuisti?
- Toiminnot LRU-välimuistissa:
- LRU-välimuistin toiminta:
- Tapoja ottaa LRU-välimuisti käyttöön:
- LRU-välimuistin toteutus jonon ja tiivistyksen avulla:
- LRU-välimuistin toteutus käyttämällä kaksoislinkitettyä luetteloa ja tiivistystä:
- LRU-välimuistin toteutus Deque & Hashmapin avulla:
- LRU-välimuistin toteutus Stack & Hashmapin avulla:
- LRU-välimuisti käyttämällä laskurin toteutusta:
- LRU-välimuistin toteutus Lazy Updatesin avulla:
- LRU-välimuistin monimutkaisuusanalyysi:
- LRU-välimuistin edut:
- LRU-välimuistin haitat:
- LRU-välimuistin reaalimaailman sovellus:
LRU Algoritmi on tavallinen ongelma ja se voi vaihdella tarpeen mukaan esimerkiksi käyttöjärjestelmissä LRU Sillä on ratkaiseva rooli, koska sitä voidaan käyttää sivun vaihtoalgoritmina sivuvirheiden minimoimiseksi.
Toiminnot LRU-välimuistissa:
- LRUCache (kapasiteetti c): Alusta LRU-välimuisti positiivisella kokokapasiteetilla c.
- saada (avain) : Palauttaa avaimen ' k' jos se on välimuistissa, muuten se palauttaa -1. Päivittää myös LRU-välimuistin tietojen prioriteetin.
- laittaa (avain, arvo): Päivitä avaimen arvo, jos se on olemassa. Muussa tapauksessa lisää avain-arvo-pari välimuistiin. Jos avainten määrä ylitti LRU-välimuistin kapasiteetin, hylkää viimeksi käytetty avain.
LRU-välimuistin toiminta:
Oletetaan, että meillä on LRU-välimuisti, jonka kapasiteetti on 3, ja haluaisimme suorittaa seuraavat toiminnot:
- Laita (avain=1, arvo=A) välimuistiin
- Laita (avain=2, arvo=B) välimuistiin
- Laita (avain=3, arvo=C) välimuistiin
- Hae (avain=2) välimuistista
- Hae (avain=4) välimuistista
- Laita (avain=4, arvo=D) välimuistiin
- Laita (avain=3, arvo=E) välimuistiin
- Hae (avain=4) välimuistista
- Laita (avain=1, arvo=A) välimuistiin
Yllä olevat toiminnot suoritetaan peräkkäin alla olevan kuvan mukaisesti:
Yksityiskohtainen selitys jokaisesta toiminnasta:
- Laita (avain 1, arvo A) : Koska LRU-välimuistin kapasiteetti on tyhjä = 3, vaihtoa ei tarvita ja laitamme {1 : A} yläosaan, eli {1 : A} on korkein prioriteetti.
- Laita (avain 2, arvo B) : Koska LRU-välimuistin kapasiteetti on tyhjä = 2, vaihtoa ei enää tarvita, mutta nyt {2 : B}:lla on korkein prioriteetti ja prioriteetti {1 : A} pienenee.
- Laita (avain 3, arvo C) : Välimuistissa on silti 1 tyhjä tila, joten laita {3 : C} ilman vaihtoa, huomaa nyt, että välimuisti on täynnä ja nykyinen tärkeysjärjestys korkeimmasta pienimpään on {3:C}, {2:B }, {1:A}.
- Hanki (avain 2) : Nyt palauta arvo avain=2 tämän toiminnon aikana, koska myös avain=2 on käytössä, uusi prioriteettijärjestys on nyt {2:B}, {3:C}, {1:A}
- Hanki (avain 4): Huomaa, että avainta 4 ei ole välimuistissa, palaamme tälle toiminnolle '-1'.
- Laita (avain 4, arvo D) : Huomaa, että välimuisti on TÄYNNÄ. Käytä nyt LRU-algoritmia määrittääksesi, mikä avain on viimeksi käytetty. Koska {1:A} oli pienin prioriteetti, poista {1:A} välimuististamme ja laita {4:D} välimuistiin. Huomaa, että uusi prioriteettijärjestys on {4:D}, {2:B}, {3:C}
- Laita (avain 3, arvo E) : Koska avain=3 oli jo olemassa välimuistissa, jossa on arvo=C, joten tämä toiminto ei johda minkään avaimen poistamiseen, vaan se päivittää avain=3:n arvoksi ' JA' . Nyt uudeksi tärkeysjärjestykseksi tulee {3:E}, {4:D}, {2:B}
- Hanki (avain 4) : Palauta avain=4. Nyt uudeksi prioriteetiksi tulee {4:D}, {3:E}, {2:B}
- Laita (avain 1, arvo A) : Koska välimuistimme on TÄYNNÄ, käytä LRU-algoritmiamme määrittääksesi, mitä avainta on käytetty vähiten äskettäin, ja koska {2:B} oli vähiten prioriteetti, poista {2:B} välimuististamme ja laita {1:A} kätkö. Nyt uusi prioriteettijärjestys on {1:A}, {4:D}, {3:E}
Tapoja ottaa LRU-välimuisti käyttöön:
LRU-välimuisti voidaan toteuttaa useilla tavoilla, ja jokainen ohjelmoija voi valita erilaisen lähestymistavan. Alla on kuitenkin yleisesti käytettyjä lähestymistapoja:
- LRU käyttäen jonoa ja hajautustoimintoa
- LRU käyttää Kaksoislinkitetty luettelo + hajautus
- LRU käyttäen Deque
- LRU käyttämällä pinoa
- LRU käyttää Vastatoteutus
- LRU käyttäen Lazy Updates -päivityksiä
LRU-välimuistin toteutus jonon ja tiivistyksen avulla:
Käytämme kahta tietorakennetta LRU-välimuistin toteuttamiseen.
- Jonottaa toteutetaan kaksoislinkitetyn luettelon avulla. Jonon enimmäiskoko on yhtä suuri kuin käytettävissä olevien kehysten kokonaismäärä (välimuistin koko). Viimeksi käytetyt sivut ovat lähellä etuosaa ja vähiten käytetyt sivut lähellä takaosaa.
- Hash sivunumero avaimena ja vastaavan jonosolmun osoite arvona.
Kun sivuun viitataan, haluttu sivu voi olla muistissa. Jos se on muistissa, meidän on irrotettava luettelon solmu ja tuotava se jonon etupuolelle.
Jos tarvittava sivu ei ole muistissa, tuomme sen muistiin. Yksinkertaisesti sanottuna lisäämme uuden solmun jonon etuosaan ja päivitämme vastaavan solmun osoitteen hashissa. Jos jono on täynnä, eli kaikki kehykset ovat täynnä, poistamme solmun jonon takaosasta ja lisäämme uuden solmun jonon etupuolelle.
Kuva:
Mietitään operaatioita, Viittaa avain x kanssa LRU-välimuistissa: { 1, 2, 3, 4, 1, 2, 5, 1, 2, 3 }
Huomautus: Aluksi muistissa ei ole sivua.Alla olevat kuvat näyttävät vaihe vaiheelta yllä olevien toimintojen suorittamisen LRU-välimuistissa.
![]()
Algoritmi:
- Luo luokka LRUCache, joka ilmoittaa luettelosta tyypin int, järjestämättömän tyypin kartan
, ja muuttuja välimuistin enimmäiskoon tallentamiseen - LRUcachen referenssitoiminnossa
- Jos tätä arvoa ei ole jonossa, työnnä tämä arvo jonon eteen ja poista viimeinen arvo, jos jono on täynnä
- Jos arvo on jo olemassa, poista se jonosta ja työnnä se jonon eteen
- Kun näyttötoiminto tulostaa, LRUcache käyttäen jonoa alkaen edestä
Alla on yllä olevan lähestymistavan toteutus:
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->sivuNumber = sivunumero;>> > 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->count = 0;>> > // Number of frames that can be stored in memory> > queue->kehysten lukumäärä = kehysten lukumäärä;>> > // Create an array of pointers for referring queue nodes> > hash->matriisi>> > return> hash;> }> > // A function to check if there is slot available in memory> int> AreAllFramesFull(Queue* queue)> {> > return> queue->count == queue-> numberOframes;>> > // A utility function to delete a frame from queue> void> deQueue(Queue* queue)> {> > if> (isQueueEmpty(queue))> > return> ;> > > // If this is the only node in list, then change front> > if> (queue->edessä == jono->taka)> > queue->edessä = NULL;>> queue->taka = jono->taka->edellinen;>> > free> (temp);> > > // decrement the number of full frames by 1> > queue->Kreivi--;>> // 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[jono->taka->sivunumero] = NULL;>> > // If queue is empty, change both front and rear> > // pointers> > if> (isQueueEmpty(queue))> > queue->taka = jono->etu = lämpötila;>> queue->edessä = lämpötila;>> > // increment number of full frames> > queue->count++;>> // 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->taulukko[sivunumero];>> if> (reqPage->seuraava)>> > // If the requested page is rear, then change rear> > // as this node will be moved to front> > if> (reqPage == queue->takaosa) {> > queue->taka = reqPage->edellinen;>> }> > > // Put the requested page before current front> > reqPage->seuraava = jono->etu;>> > // Change prev of current front> > reqPage->seuraava->edellinen = reqPage;>> }> }> > // Driver code> int> main()> {> > // Let cache can hold 4 pages> > Queue* q = createQueue(4);> > > // Let 10 different pages can be requested (pages to be> > // referenced are numbered from 0 to 9> > Hash* hash = createHash(10);> > > // Let us refer pages 1, 2, 3, 1, 4, 5> > ReferencePage(q, hash, 1);> > ReferencePage(q, hash, 2);> > ReferencePage(q, hash, 3);> > ReferencePage(q, hash, 1);> > ReferencePage(q, hash, 4);> > ReferencePage(q, hash, 5);> > > // Let us print cache frames after the above referenced> > // pages> > printf> (> '%d '> , q->etu->sivunumero);>> , q->etu->seuraava->sivunumero);>> , q->etu->seuraava->seuraava->sivunumero);>> , q->etu->seuraava->seuraava->seuraava->sivunumero);>> |