Užbaikite LRU talpyklos su įgyvendinimais mokymo programą
Kas yra LRU talpykla?
Talpyklos keitimo algoritmai yra efektyviai sukurti taip, kad pakeistų talpyklą, kai vieta pilna. The Neseniai naudotas (LRU) yra vienas iš tų algoritmų. Kaip rodo pavadinimas, kai talpyklos atmintis yra pilna, LRU parenka duomenis, kurie buvo neseniai naudojami, ir pašalina juos, kad būtų vietos naujiems duomenims. Duomenų prioritetas talpykloje keičiasi atsižvelgiant į tų duomenų poreikį, t. y. jei kai kurie duomenys būtų paimti arba atnaujinti neseniai, tų duomenų prioritetas būtų pakeistas ir priskiriamas aukščiausiam prioritetui, o duomenų prioritetas mažėja, jei lieka nepanaudotos operacijos po operacijų.
Turinys
- Kas yra LRU talpykla?
- Operacijos LRU talpykloje:
- LRU talpyklos veikimas:
- LRU talpyklos diegimo būdai:
- LRU talpyklos diegimas naudojant eilę ir maišą:
- LRU talpyklos diegimas naudojant dvigubai susietą sąrašą ir maišą:
- LRU talpyklos diegimas naudojant Deque & Hashmap:
- LRU talpyklos diegimas naudojant Stack & Hashmap:
- LRU talpykla naudojant Counter Implementation:
- LRU talpyklos diegimas naudojant „Lazy Updates“:
- LRU talpyklos sudėtingumo analizė:
- LRU talpyklos privalumai:
- LRU talpyklos trūkumai:
- Realus LRU talpyklos taikymas:
LRU algoritmas yra standartinė problema ir jis gali keistis pagal poreikį, pavyzdžiui, operacinėse sistemose LRU vaidina lemiamą vaidmenį, nes gali būti naudojamas kaip puslapio pakeitimo algoritmas, siekiant sumažinti puslapio gedimus.
Operacijos LRU talpykloje:
- LRUCache (c talpa): Inicijuoti LRU talpyklą su teigiamo dydžio talpa c.
- gauti (raktą) : grąžina rakto reikšmę k' jei jis yra talpykloje, kitu atveju jis grąžina -1. Taip pat atnaujinamas LRU talpykloje esančių duomenų prioritetas.
- įdėti (raktas, vertė): Atnaujinkite rakto reikšmę, jei toks raktas yra, Kitu atveju pridėkite rakto ir vertės porą prie talpyklos. Jei raktų skaičius viršijo LRU talpyklos talpą, atmeskite neseniai naudotą raktą.
LRU talpyklos veikimas:
Tarkime, kad turime 3 talpos LRU talpyklą ir norėtume atlikti šias operacijas:
- Įdėkite (key=1, value=A) į talpyklą
- Įdėkite (raktas = 2, vertė = B) į talpyklą
- Įdėkite (key=3, value=C) į talpyklą
- Gaukite (key=2) iš talpyklos
- Gaukite (key=4) iš talpyklos
- Įdėkite (key=4, value=D) į talpyklą
- Įdėkite (key=3, value=E) į talpyklą
- Gaukite (key=4) iš talpyklos
- Įdėkite (key=1, value=A) į talpyklą
Aukščiau nurodytos operacijos atliekamos viena po kitos, kaip parodyta paveikslėlyje žemiau:
Išsamus kiekvienos operacijos paaiškinimas:
- Įdėti (1 raktas, vertė A) : Kadangi LRU talpyklos talpa yra tuščia = 3, nereikia keisti ir mes įdedame {1 : A} viršuje, t. y. {1 : A} turi aukščiausią prioritetą.
- Įdėkite (2 raktas, B vertė) : Kadangi LRU talpyklos talpa tuščia = 2, vėl nereikia keisti, bet dabar {2 : B} turi aukščiausią prioritetą, o prioritetas {1 : A} sumažėja.
- Įdėti (3 raktas, vertė C) : Vis dar yra 1 laisva vieta talpykloje, todėl įdėkite {3 : C} be jokių pakeitimų, pastebėkite, kad dabar talpykla pilna ir dabartinė prioritetų tvarka nuo aukščiausio iki mažiausio yra {3:C}, {2:B }, {1:A}.
- Gaukite (2 raktas) : Dabar šios operacijos metu grąžinkite rakto = 2 reikšmę, taip pat kadangi naudojamas raktas = 2, dabar nauja prioritetų tvarka yra {2:B}, {3:C}, {1:A}
- Gaukite (4 raktas): Stebėkite, kad 4 klavišo talpykloje nėra, šiai operacijai grąžiname „-1“.
- Įdėti (4 raktas, reikšmė D) : Stebėkite, ar talpykla pilna, dabar naudokite LRU algoritmą, kad nustatytumėte, kuris raktas buvo naudojamas mažiausiai neseniai. Kadangi {1:A} turėjo mažiausią prioritetą, pašalinkite {1:A} iš talpyklos ir įdėkite {4:D} į talpyklą. Atkreipkite dėmesį, kad nauja prioriteto tvarka yra {4:D}, {2:B}, {3:C}
- Įdėti (3 raktas, vertė E) : Kadangi key=3 jau buvo talpykloje su value=C, todėl ši operacija nepašalins jokio rakto, o atnaujins key=3 reikšmę į „ IR' . Dabar nauja prioriteto tvarka taps {3:E}, {4:D}, {2:B}
- Gaukite (4 klavišas) : grąžina rakto reikšmę = 4. Dabar naujas prioritetas taps {4:D}, {3:E}, {2:B}
- Įdėti (1 raktas, vertė A) : Kadangi mūsų talpykla PILNA, todėl naudokite mūsų LRU algoritmą, kad nustatytumėte, kuris raktas buvo naudojamas mažiausiai neseniai, o kadangi {2:B} turėjo mažiausią prioritetą, pašalinkite {2:B} iš talpyklos ir įdėkite {1:A} į talpykla. Dabar nauja prioriteto tvarka yra {1:A}, {4:D}, {3:E}
LRU talpyklos diegimo būdai:
LRU talpykla gali būti įdiegta įvairiais būdais, ir kiekvienas programuotojas gali pasirinkti skirtingą metodą. Tačiau toliau pateikiami dažniausiai naudojami metodai:
- LRU naudojant eilę ir maišą
- LRU naudojant Dvigubai susietas sąrašas + maiša
- LRU naudojant Deque
- LRU naudojant Stack
- LRU naudojant Skaitiklio įgyvendinimas
- LRU naudojant Lazy Updates
LRU talpyklos diegimas naudojant eilę ir maišą:
Mes naudojame dvi duomenų struktūras, kad įdiegtume LRU talpyklą.
- Eilė yra įgyvendinamas naudojant dvigubai susietą sąrašą. Didžiausias eilės dydis bus lygus bendram galimų kadrų skaičiui (talpyklos dydžiui). Paskutiniai naudoti puslapiai bus šalia priekinės dalies, o rečiausiai naudoti puslapiai bus netoli galinės dalies.
- A Hašas su puslapio numeriu kaip raktu ir atitinkamo eilės mazgo adresu kaip reikšmė.
Kai pateikiama nuoroda į puslapį, reikiamas puslapis gali būti atmintyje. Jei jis yra atmintyje, turime atskirti sąrašo mazgą ir perkelti jį į eilės priekį.
Jei reikiamo puslapio atmintyje nėra, įnešame jį į atmintį. Paprastais žodžiais tariant, eilės priekyje pridedame naują mazgą ir atnaujiname atitinkamo mazgo adresą maišoje. Jei eilė pilna, t.y. visi kadrai pilni, pašaliname mazgą iš eilės galo, o naują mazgą įtraukiame į eilės priekį.
Iliustracija:
Apsvarstykime operacijas, Nurodo Raktas x su LRU talpykloje: { 1, 2, 3, 4, 1, 2, 5, 1, 2, 3 }
Pastaba: Iš pradžių atmintyje nėra puslapio.Toliau pateiktuose vaizduose žingsnis po žingsnio parodytas aukščiau nurodytų operacijų vykdymas LRU talpykloje.
![]()
Algoritmas:
- Sukurkite klasę LRUCache su deklaruodami int tipo sąrašą, netvarkingą tipo žemėlapį
, ir kintamasis, skirtas saugoti maksimalų talpyklos dydį - LRUCache nuoroda funkcijoje
- Jei šios reikšmės eilėje nėra, stumkite šią reikšmę prieš eilę ir pašalinkite paskutinę reikšmę, jei eilė pilna
- Jei reikšmė jau yra, pašalinkite ją iš eilės ir įstumkite į eilės priekį
- Ekrano funkcijoje spausdinama LRUCache naudojant eilę pradedant nuo priekio
Toliau pateikiamas pirmiau minėto metodo įgyvendinimas:
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 = puslapioNumber;>> > 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->skaičius = 0;>> > // Number of frames that can be stored in memory> > queue->skaičiusOframes = skaičiusOframes;>> > // Create an array of pointers for referring queue nodes> > hash->masyvas>> (QNode*));> > > // Initialize all hash entries as empty> > int> i;> > for> (i = 0; i capacity; ++i)> > hash->masyvas[i] = NULL;>> > // A utility function to check if queue is empty> int> isQueueEmpty(Queue* queue)> {> > return> queue->galinis == NULL;>> queue->priekis = NULL;>> queue->galinis = eilė->galis->ankstesnis;>> queue->galinis->kitas = 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->masyvas[eilė->gale->puslapio numeris] = NULL;>> > // If queue is empty, change both front and rear> > // pointers> > if> (isQueueEmpty(queue))> > queue->galinis = eilė->priekis = temp;>> queue->priekis = temp;>> > // increment number of full frames> > queue->skaičiuoti++;>> // 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->masyvas[puslapio numeris];>> if> (reqPage->kitas)>>> > // If the requested page is rear, then change rear> > // as this node will be moved to front> > if> (reqPage == queue->gale) {> > queue->galinis = reqPage->prev;>> }> > > // Put the requested page before current front> > reqPage->kitas = eilė->priekis;>> > // Change prev of current front> > reqPage->kitas->ankstesnis = 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->front->pageNumber);>> , q->priekis->kitas->puslapio numeris);>> , q->front->ext->ext->pageNumber);>> , q->priekis->kitas->kitas->kitas->puslapio numeris);>> |