Frage

Ich versuche, LRU Cache mit C ++ zu implementieren. Ich würde gerne wissen, was das beste Design für deren Umsetzung ist. Ich weiß, LRU sollte find () zur Verfügung stellen, ein Element hinzufügen und ein Element entfernen. Das Entfernen sollte das LRU-Element entfernen. Was ist die beste ADTs dies zu implementieren Für die Ex: Wenn ich eine Karte mit dem Element als Wert und Zeitzähler als Schlüssel verwenden I in O (log n) Zeit suchen, ist Einfügen von O (n), das Löschen ist O (log n)

.
War es hilfreich?

Lösung

Ein großes Problem mit LRU-Caches ist, dass es wenig „const“ Operationen ist, die meisten werden die zugrunde liegende Darstellung ändern (wenn auch nur, weil sie das Element zugegriffen stoßen).

Das ist natürlich sehr unbequem, weil es bedeutet, dass es keine traditionellen STL-Container, und daher eine Idee Iteratoren des Ausstellens ist recht kompliziert: Wenn der Iterator dereferenziert wird dies ein Zugang ist, die die Liste ändern sollen wir Iterieren sind auf ... oh mein.

Und es gibt die Leistungen Berücksichtigung sowohl in der Bezeichnung der Geschwindigkeit und Speicherverbrauch.

Es ist bedauerlich, aber Sie werden einen Weg benötigen, um Ihre Daten in einer Warteschlange (LRU) zu organisieren (mit der Möglichkeit, Elemente aus der Mitte zu entfernen), und dies bedeutet, dass Ihre Elemente voneinander unabhängig sein müssen. Ein std::list passt, natürlich, aber es ist mehr als Sie benötigen. Eine einfach verkettete Liste ist hier ausreichend, da Sie rückwärts die Liste zu iterieren nicht brauchen (Sie wollen einfach nur eine Warteschlange, nachdem alle).

Doch ein großer Nachteil diejenigen, ist ihre schlechte Referenzlokalität, wenn Sie mehr Geschwindigkeit benötigen müssen Sie Ihren eigenen (Pool?), Um Allocator für die Knoten, so dass sie so nah beieinander wie möglich gehalten werden. Dies wird auch etwas Heapfragmentierung lindern.

Als nächstes müssen Sie natürlich eine Indexstruktur (für den Cache-Bit). Die natürlichste ist in Richtung einer Hash-Karte zu drehen. std::tr1::unordered_map, std::unordered_map oder boost::unordered_map sind in der Regel gute Qualität Implementierung, einige sollten zur Verfügung stehen. Sie verteilen auch zusätzliche Knoten für den Umgang mit Hash-Kollision, könnte man andere Arten von Hash-Karten bevorzugen Besuche Wikipedia-Artikel über das Thema und lesen Sie über die Eigenschaften der verschiedenen Implementierungstechniken.

Weiter gibt es die (offensichtlich) Threading-Unterstützung. Wenn Sie Thread-Unterstützung nicht brauchen, dann fein es ist, wenn man jedoch tun, es ist ein bisschen komplizierter:

  • , sagte Wie ich, gibt es wenig const Betrieb auf einer solchen Struktur, so dass Sie nicht wirklich lesen unterscheiden müssen / Schreibzugriffe
  • Interne Verriegelung ist in Ordnung, aber Sie werden feststellen, dass es nicht schön mit Ihren Anwendungen spielt. Das Problem mit interner Verriegelung ist, dass es nicht das Konzept der „Transaktion“ nicht unterstützt, da sie die Sperre zwischen jedem Anruf verzichten. Wenn dies der Fall ist, verwandeln Sie Ihr Objekt in ein Mutex und bietet eine std::unique_ptr<Lock> lock() Methode (im Debug können Sie behaupten, als die Sperre am Eintrittspunkt jeder Methode genommen wird)
  • Es gibt (in Sperrstrategien) die Frage der erneuten Eintritt, dh die Fähigkeit, „relock“ der Mutex aus dem gleichen Thread, überprüfen Boost.Thread für weitere Informationen über die verschiedenen Schlösser und mutexes verfügbar

Schließlich gibt es das Problem der Fehlerberichterstattung. Da erwartet wird, dass ein Cache die Daten nicht in der Lage sein kann, die Sie setzen zurückzuholen, würde ich eine Ausnahme „schlechten Geschmack“ in Betracht ziehen. Betrachten Sie entweder Zeiger (Value*) oder Boost.Optional (boost::optional<Value&>). Ich würde es vorziehen, weil Boost.Optional seine semantische klar ist.

Andere Tipps

Der beste Weg, um eine LRU zu implementieren, ist die Kombination einer std :: Liste zu verwenden und stdext :: hash_map (will nur verwenden std dann std :: map).

  • Speichern Sie die Daten in der Liste, so dass die am wenigsten vor kurzem in bei der verwendeten dauern und die Karte zu zeigen Sie auf die Verwendung Listenelemente.
  • Für „get“ verwenden, um die Karte die bekommen Liste Adr und Abrufen der Daten und bewegen Sie den aktuellen Knoten zum
    erste (wurde, da diese verwendet jetzt) ??die Karte und aktualisieren.
  • Für „Einfügen“ entfernen Sie das letzte Element aus der Liste aus und fügen Sie die neuen Daten auf der Vorderseite und die Karte aktualisieren.

Dies ist die schnellst Sie bekommen können, wenn Sie einen hash_map verwenden, sollten Sie fast alle Operationen in O (1) getan haben. Wenn std :: map sollte es dauern O (log n) in allen Fällen.

Eine sehr gute Umsetzung ist verfügbar hier

Der Artikel beschreibt ein paar C ++ LRU-Cache-Implementierungen (ein STL, eine mit boost::bimap ).

Wenn Sie Priorität sagen, denke ich „ Haufen “, die natürlich führt zu Zunahme-Taste und delete-min .

Ich würde nicht den Cache sichtbar nach außen überhaupt machen, wenn ich es vermeiden kann. Ich habe gerade eine Sammlung habe (von was auch immer) und das Caching unsichtbar zu verarbeiten, das Hinzufügen und Entfernen von Elementen nach Bedarf, aber die externe Schnittstelle wäre genau, dass die zugrunde liegenden Sammlung.

Was die Umsetzung geht, ist ein Haufen wohl die naheliegendste. Es hat Komplexitäten etwa ähnlich eine Karte, aber anstatt einen Baum von verknüpften Knoten erstellen, ordnet es Elemente in einem Array und die „Links“ implizit sind basierend auf Array-Indizes. Dadurch erhöht sich die Speicherdichte des Caches und verbessert Örtlichkeit in der "realen" (physischen) Prozessor-Cache.

Ich schlage vor, ein Haufen und vielleicht ein Fibonacci Heap

ich mit einem normalen Haufen in C ++ gehen würde.

Mit dem std :: make_heap (von der Norm garantierte O (n) sein), std :: pop_heap und std :: push_heap in # include, Implementierung wäre es absolut Kuchen sein. Sie müssen nur Sorgen über die Erhöhung schlüssel.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top