Pabeigt apmācību par LRU kešatmiņu ar ieviešanu
Kas ir LRU kešatmiņa?
Kešatmiņas aizstāšanas algoritmi ir efektīvi izstrādāti, lai aizstātu kešatmiņu, kad vieta ir pilna. The Vismazāk lietots (LRU) ir viens no šiem algoritmiem. Kā norāda nosaukums, kad kešatmiņa ir pilna, LRU atlasa datus, kas ir vismazāk izmantoti, un noņem tos, lai atbrīvotu vietu jaunajiem datiem. Kešatmiņā esošo datu prioritāte mainās atkarībā no šo datu nepieciešamības, t.i., ja daži dati tiek ielādēti vai atjaunināti nesen, tad šo datu prioritāte tiks mainīta un piešķirta augstākajai prioritātei, un datu prioritāte samazinās, ja tā paliek neizmantotas operācijas pēc operācijām.
Satura rādītājs
- Kas ir LRU kešatmiņa?
- Darbības LRU kešatmiņā:
- LRU kešatmiņas darbība:
- LRU kešatmiņas ieviešanas veidi:
- LRU kešatmiņas ieviešana, izmantojot rindu un jaukšanu:
- LRU kešatmiņas ieviešana, izmantojot divkārši saistīto sarakstu un jaukšanu:
- LRU kešatmiņas ieviešana, izmantojot Deque & Hashmap:
- LRU kešatmiņas ieviešana, izmantojot Stack & Hashmap:
- LRU kešatmiņa, izmantojot Counter Implementation:
- LRU kešatmiņas ieviešana, izmantojot Lazy Updates:
- LRU kešatmiņas sarežģītības analīze:
- LRU kešatmiņas priekšrocības:
- LRU kešatmiņas trūkumi:
- LRU kešatmiņas reālā lietojumprogramma:
LRU algoritms ir standarta problēma, un tas var mainīties atkarībā no vajadzības, piemēram, operētājsistēmās LRU Tam ir izšķiroša nozīme, jo to var izmantot kā lapas aizstāšanas algoritmu, lai samazinātu lappušu kļūdas.
Darbības LRU kešatmiņā:
- LRUCache (ietilpība c): Inicializējiet LRU kešatmiņu ar pozitīva izmēra ietilpību c.
- saņemt (atslēgu) : atgriež atslēgas vērtību k' ja tas atrodas kešatmiņā, pretējā gadījumā tas atgriež -1. Atjaunina arī datu prioritāti LRU kešatmiņā.
- likt (atslēga, vērtība): Atjauniniet atslēgas vērtību, ja šī atslēga pastāv, pretējā gadījumā pievienojiet atslēgas vērtību pāri kešatmiņai. Ja atslēgu skaits pārsniedz LRU kešatmiņas ietilpību, noraidiet vismazāk izmantoto atslēgu.
LRU kešatmiņas darbība:
Pieņemsim, ka mums ir LRU kešatmiņa ar ietilpību 3, un mēs vēlētos veikt šādas darbības:
- Ievietojiet (key=1, value=A) kešatmiņā
- Ievietojiet (atslēga = 2, vērtība = B) kešatmiņā
- Ievietojiet (key=3, value=C) kešatmiņā
- Iegūstiet (atslēga=2) no kešatmiņas
- Iegūstiet (atslēga=4) no kešatmiņas
- Ievietojiet (key=4, value=D) kešatmiņā
- Ievietojiet (key=3, value=E) kešatmiņā
- Iegūstiet (atslēga=4) no kešatmiņas
- Ievietojiet (key=1, value=A) kešatmiņā
Iepriekš minētās darbības tiek veiktas viena pēc otras, kā parādīts zemāk esošajā attēlā:
Detalizēts katras darbības skaidrojums:
- Put (1. atslēga, A vērtība) : Tā kā LRU kešatmiņā ir tukša ietilpība = 3, nav nepieciešama nomaiņa, un mēs ievietojam {1 : A} augšpusē, t.i., {1 : A} ir augstākā prioritāte.
- Put (2. atslēga, B vērtība) : Tā kā LRU kešatmiņā ir tukša ietilpība = 2, atkal nav nepieciešama nomaiņa, bet tagad {2 : B} ir augstākā prioritāte un prioritāte ir {1 : A} samazinājums.
- Put (3. atslēga, vērtība C) : Kešatmiņā joprojām ir 1 brīva vieta, tāpēc ievietojiet {3 : C} bez aizstāšanas, ievērojiet, ka tagad kešatmiņa ir pilna un pašreizējā prioritātes secība no augstākās līdz zemākajai ir {3:C}, {2:B }, {1:A}.
- Iegūt (2. atslēga) : Tagad šīs darbības laikā atgriežat atslēgas vērtību 2, arī tāpēc, ka tiek izmantota atslēga 2, tagad jaunā prioritātes secība ir {2:B}, {3:C}, {1:A}
- Iegūt (4. atslēga): Ievērojiet, ka atslēgas 4 nav kešatmiņā, mēs atgriežam “-1” šai darbībai.
- Put (4. atslēga, D vērtība) : ievērojiet, vai kešatmiņa ir PILNA, tagad izmantojiet LRU algoritmu, lai noteiktu, kura atslēga ir izmantota vismazāk. Tā kā {1:A} bija vismazākā prioritāte, noņemiet {1:A} no mūsu kešatmiņas un ievietojiet {4:D} kešatmiņā. Ņemiet vērā, ka jaunā prioritātes secība ir {4:D}, {2:B}, {3:C}
- Put (3. atslēga, vērtība E) : Tā kā atslēga = 3 jau bija kešatmiņā ar vērtību = C, šī darbība neizraisīs nevienas atslēgas noņemšanu, bet gan atjauninās atslēgas = 3 vērtību uz ' UN' . Tagad jaunā prioritātes secība kļūs par {3:E}, {4:D}, {2:B}
- Iegūt (4. atslēga) : atgriež atslēgas vērtību =4. Tagad jaunā prioritāte būs {4:D}, {3:E}, {2:B}
- Put (1. atslēga, A vērtība) : Tā kā mūsu kešatmiņa ir PILNA, izmantojiet mūsu LRU algoritmu, lai noteiktu, kura atslēga tika izmantota vismazāk, un, tā kā atslēgai {2:B} bija vismazākā prioritāte, noņemiet {2:B} no mūsu kešatmiņas un ievietojiet {1:A} kešatmiņa. Tagad jaunā prioritātes secība ir {1:A}, {4:D}, {3:E}
LRU kešatmiņas ieviešanas veidi:
LRU kešatmiņu var ieviest dažādos veidos, un katrs programmētājs var izvēlēties citu pieeju. Tomēr tālāk ir norādītas visbiežāk izmantotās metodes:
- LRU, izmantojot rindu un jaukšanu
- LRU izmantojot Divkārši saistīts saraksts + jaukšana
- LRU, izmantojot Deque
- LRU, izmantojot Stack
- LRU izmantojot Counter ieviešana
- LRU, izmantojot Lazy Updates
LRU kešatmiņas ieviešana, izmantojot rindu un jaukšanu:
Mēs izmantojam divas datu struktūras, lai ieviestu LRU kešatmiņu.
- Rinda tiek ieviests, izmantojot divkārši saistītu sarakstu. Maksimālais rindas lielums būs vienāds ar kopējo pieejamo kadru skaitu (kešatmiņas lielums). Pēdējās izmantotās lapas atradīsies tuvu priekšpusei un vismazāk izmantotās lapas būs tuvu aizmugurei.
- Hašs ar lapas numuru kā atslēgu un atbilstošā rindas mezgla adresi kā vērtību.
Atsaucoties uz lapu, vajadzīgā lapa var būt atmiņā. Ja tas ir atmiņā, mums ir jāatvieno saraksta mezgls un jānogādā tas rindas priekšgalā.
Ja vajadzīgā lapa nav atmiņā, mēs to ievietojam atmiņā. Vienkāršiem vārdiem sakot, mēs pievienojam jaunu mezglu rindas priekšpusē un atjauninām atbilstošo mezgla adresi jaucējkodā. Ja rinda ir pilna, t.i., visi kadri ir pilni, mēs noņemam mezglu no rindas aizmugures un pievienojam jauno mezglu rindas priekšpusē.
Ilustrācija:
Apskatīsim operācijas, Atsaucas taustiņu x ar LRU kešatmiņā: { 1, 2, 3, 4, 1, 2, 5, 1, 2, 3 }
Piezīme: Sākotnēji atmiņā nav nevienas lapas.Zemāk redzamajos attēlos soli pa solim parādīta iepriekš minēto darbību izpilde LRU kešatmiņā.
![]()
Algoritms:
- Izveidojiet klasi LRUCache, deklarējot int tipa sarakstu, nesakārtotu tipa karti
, un mainīgais, lai saglabātu maksimālo kešatmiņas lielumu - LRUcache atsauces funkcijā
- Ja šīs vērtības rindā nav, nospiediet šo vērtību rindas priekšā un noņemiet pēdējo vērtību, ja rinda ir pilna
- Ja vērtība jau ir, noņemiet to no rindas un ievietojiet to rindas priekšpusē
- Displeja funkcijā drukājiet LRUCache, izmantojot rindu, sākot no priekšpuses
Tālāk ir aprakstīta iepriekš minētās pieejas īstenošana.
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 = lapasNumurs;>> > 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->skaits = 0;>> > // Number of frames that can be stored in memory> > queue->skaitsOframes = skaitsOframes;>> > // Create an array of pointers for referring queue nodes> > hash->masīvs>> (QNode*));> > > // Initialize all hash entries as empty> > int> i;> > for> (i = 0; i capacity; ++i)> > hash->masīvs[i] = NULL;>> > // A utility function to check if queue is empty> int> isQueueEmpty(Queue* queue)> {> > return> queue->aizmugure == NULL;>> > // Change rear and remove the previous rear> > QNode* temp = queue->aizmugure;>> > if> (queue->aizmugurē)>> > free> (temp);> > > // decrement the number of full frames by 1> > queue->skaitīt —;>> // 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->masīvs[rinda->aizmugure->lapas numurs] = NULL;>> > // If queue is empty, change both front and rear> > // pointers> > if> (isQueueEmpty(queue))> > queue->aizmugure = rinda->priekšpuse = temp;>> queue->priekšpuse = temp;>> > // increment number of full frames> > queue->skaitīt++;>> // 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->masīvs[lapasNumurs];>> if> (reqPage->nākamais)>> > // If the requested page is rear, then change rear> > // as this node will be moved to front> > if> (reqPage == queue->aizmugurē) {> > queue->aizmugure = reqPage->iepriekšējā;>> }> > > // Put the requested page before current front> > reqPage->nākamais = rinda->priekšpuse;>> > // Change prev of current front> > reqPage->nākamais->iepriekšējais = 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->priekšā->nākamais->lapas numurs);>> , q->priekšā->nākamais->nākamais->lapas numurs);>> , q->priekšā->nākamais->nākamais->nākamais->lapas numurs);>> |