2010-01-13 25 views
28

Ho un codice C++ dove devo implementare la sostituzione della cache usando la tecnica LRU.
Finora so due metodi per implementare LRU sostituzione della cache:Implementazione LRU nel codice di produzione

  1. Utilizzando timestamp per ogni volta che i dati memorizzati nella cache si accede e, infine, confrontando i timestamp al momento della sostituzione.
  2. Utilizzando una pila di elementi memorizzati nella cache e spostandoli verso l'alto se sono accessibili di recente, così alla fine il fondo conterrà la candidata LRU.

Quindi, quale di questi è meglio utilizzare nel codice di produzione?
I loro altri metodi migliori?

+0

Cosa intendi per "migliore": devi specificare quali sono i tuoi criteri. Inoltre, dai un'occhiata a questa domanda http://stackoverflow.com/questions/1935777/c-design-how-to-cache-most-recent-used –

+0

Invece di timestamp, puoi usare intero per tenere traccia. Quando si accede a un elemento, rendilo 0 e incrementa altre tracce – user

+1

@user che sarebbe un design scarso perché renderebbe il costo di accesso 'O (n)' – Alnitak

risposta

39

Recentemente ho implementato una cache LRU utilizzando un elenco collegato su una mappa hash.

/// Typedef for URL/Entry pair 
    typedef std::pair< std::string, Entry > EntryPair; 

    /// Typedef for Cache list 
    typedef std::list<EntryPair> CacheList; 

    /// Typedef for URL-indexed map into the CacheList 
    typedef boost::unordered_map< std::string, CacheList::iterator > CacheMap; 

    /// Cache LRU list 
    CacheList mCacheList; 

    /// Cache map into the list 
    CacheMap mCacheMap; 

Ha il vantaggio di essere O (1) per tutte le operazioni importanti.

L'algoritmo di inserimento:

// create new entry 
Entry iEntry(...); 

// push it to the front; 
mCacheList.push_front(std::make_pair(aURL, iEntry)); 

// add it to the cache map 
mCacheMap[ aURL ] = mCacheList.begin(); 

// increase count of entries 
mEntries++; 

// check if it's time to remove the last element 
if (mEntries > mMaxEntries) 
{ 
    // erease from the map the last cache list element 
    mCacheMap.erase(mCacheList.back().first); 

    // erase it from the list 
    mCacheList.pop_back(); 

    // decrease count 
    mEntries--; 
} 
+0

Ho implementato anche il caching LRU e uso una tecnica simile. Una ricerca non è più const, ovviamente quando cambia stato e quindi in un ambiente multi-thread hai bisogno di blocchi esclusivi per ogni accesso anche a più lettori. – CashCow

+0

perché è una risposta valida? considera, la dimensione della cache è di 5 elementi. E il flusso di input è 1,2,3,4,5. a questo punto la lista collegata avrà questo ordine 5-> 4-> 3-> 2-> 1. Ora quando viene inserito un nuovo elemento già esistente, diciamo '1', quindi l'elenco diventa 1 -> 5-> 4-> 3-> 2-> 1. E l'hash (tasto = 1) viene sovrascritto e punti all'inizio della lista. Dato che la cache è over-limit, devi eliminare l'elemento Hash (key = 1) e pop dall'elenco. Pertanto, l'elenco sarà 1 -> 5 -> 4-> 3-> 2 ma l'hash conterrà solo 4 elementi – blueskin

+1

ciò che manca a questo codice è, quando si inserisce, se un elemento esistente viene inserito, la precedente occorrenza dovrebbe essere rimosso dalla lista e l'hash prima di inserire – blueskin

2

Nel nostro ambiente di produzione usiamo C++ elenco doppia linked che è simile al Linux kernel linked list. Il bello è che puoi aggiungere un oggetto a tutti gli elenchi collegati che desideri e l'operazione di elenco è veloce e semplice.

5

Per semplicità, forse dovresti prendere in considerazione l'utilizzo della mappa MultiIndex di Boost. Se separiamo la chiave dai dati, supportiamo più set di chiavi sugli stessi dati.

Da [http://old.nabble.com/realization-of-Last-Recently-Used-cache-with-boost%3A%3Amulti_index-td22326432.html]:

" ... usare due indici: 1) per la ricerca di hashing valore tasto 2) per il monitoraggio sequenziale articoli ultima usati di recente (ottenere funzione di mettere elemento come ultimo elemento in sequesnce Se. dobbiamo rimuovere alcuni elementi dalla cache, potremmo eliminarli dall'inizio della sequenza). "

Si noti che l'operatore "progetto" "consente al programmatore di spostarsi tra diversi indici dello stesso multi_index_container" in modo efficiente.

+1

Vedere anche un esempio nella documentazione di boost :: multi_index: http://www.boost.org/doc/libs/1_45_0/libs/multi_index/doc/examples.html#example9 –

3

Questo article descrive l'implementazione utilizzando una coppia di contenitori STL (una mappa valore-chiave più un elenco per la cronologia di accesso chiave) o un singolo boost::bimap.

+1

Grazie; aggiustato. Il collegamento – timday

+0

è morto? – Gregory

+0

@Gregory: bitbucket sembra aver cambiato qualcosa sull'hosting statico da un repository. Puoi trovare il documento su https://bitbucket.org/timday/timday.bitbucket.org/src/ ma dovresti scaricarlo (e accompagnare css) o il pdf. Vedremo se qualcosa può essere fatto ... – timday