CPU-Cache

  Dies ist ein hervorgehobener Artikel. Klicken Sie hier für weitere Informationen.   Diagramm eines CPU-Speichercaches Diagramm eines CPU-Speichercaches

EIN CPU-Cache ist ein Cache, der von der verwendet wird Zentraleinheit von a Computer um die durchschnittliche Zugriffszeit auf den Speicher zu reduzieren. Der Cache ist ein kleinerer, schnellerer Speicher, der Kopien der Daten von den am häufigsten verwendeten Hauptspeicherorten speichert. Solange die meisten Speicherzugriffe auf gecachte Speicherorte erfolgen, wird die durchschnittliche Latenzzeit von Speicherzugriffen näher an der Cache-Latenzzeit liegen als an der Latenzzeit des Hauptspeichers.

Das Diagramm rechts zeigt zwei Speicher. Jeder Ort in jedem Speicher hat ein Datum (a Cache-Zeile ), die in verschiedenen Ausführungen zwischen 8 und 512 Byte groß ist. Die Größe der Cache-Zeile ist normalerweise größer als die Größe des üblichen Zugriffs, der von einem CPU-Befehl angefordert wird, der von 1 bis 16 Bytes reicht. Jeder Ort in jedem Speicher hat auch einen Index, der eine eindeutige Nummer ist, die verwendet wird, um auf diesen Ort zu verweisen. Der Index für eine Stelle im Hauptspeicher wird als Adresse bezeichnet. Jede Stelle im Cache hat ein Tag, das den Index des Datums im Hauptspeicher enthält, das zwischengespeichert wurde. Im Datencache einer CPU werden diese Einträge aufgerufen Cache-Zeilen oder Cache-Blöcke .

Wenn der Prozessor eine Stelle im Hauptspeicher lesen oder schreiben möchte, prüft er zuerst, ob sich diese Speicherstelle im Cache befindet. Dies wird erreicht, indem die Adresse des Speicherplatzes mit allen Tags im Cache verglichen wird, die diese Adresse enthalten könnten. Wenn der Prozessor feststellt, dass sich die Speicherstelle im Cache befindet, sagen wir, dass a Cache-Treffer aufgetreten ist, ansonsten spricht man von a Cache-Miss . Im Fall eines Cache-Treffers liest oder schreibt der Prozessor die Daten sofort in die Cache-Zeile. Der Anteil der Zugriffe, die zu einem Cache-Treffer führen, wird als bezeichnet Trefferquote , und ist ein Maß für die Effektivität des Caches.



Im Fall eines Cache-Fehltreffers weisen die meisten Caches einen neuen Eintrag zu, der das gerade verpasste Tag und eine Kopie der Daten aus dem Speicher umfasst. Der Verweis kann dann wie bei einem Treffer auf den neuen Eintrag angewendet werden. Misses sind langsam, weil sie erfordern, dass die Daten vom Hauptspeicher übertragen werden. Diese Übertragung verursacht eine Verzögerung, da der Hauptspeicher viel langsamer ist als der Cache-Speicher.

Einige Details zur Bedienung

Um Platz für den neuen Eintrag bei einem Cache-Fehlschlag zu schaffen, muss der Cache im Allgemeinen dies tun vertreiben einer der vorhandenen Einträge. Die Heuristik, die zum Auswählen des zu entfernenden Eintrags verwendet wird, heißt the Ersatzpolitik . Das grundlegende Problem bei jeder Ersetzungsrichtlinie besteht darin, dass sie vorhersagen muss, welcher vorhandene Cache-Eintrag in Zukunft am wenigsten wahrscheinlich verwendet wird. Die Zukunft vorherzusagen ist schwierig, insbesondere für Hardware-Caches, die einfache Regeln verwenden, die der Implementierung in Schaltungen zugänglich sind, so dass es eine Vielzahl von Ersatzrichtlinien zur Auswahl gibt und keine perfekte Möglichkeit, sich zwischen ihnen zu entscheiden. Eine beliebte Ersetzungsrichtlinie, LRU, ersetzt den am längsten nicht verwendeten Eintrag.

Wenn Daten in den Cache geschrieben werden, müssen sie irgendwann auch in den Hauptspeicher geschrieben werden. Das Timing dieses Schreibvorgangs wird durch das gesteuert, was als bekannt ist Politik schreiben . In einem durchschreiben Cache, jeder Schreibvorgang in den Cache verursacht einen Schreibvorgang in den Hauptspeicher. Alternativ in a Schreib zurück Cache werden Schreibvorgänge nicht sofort in den Speicher gespiegelt. Stattdessen verfolgt der Cache, welche Orte überschrieben wurden (diese Orte sind markiert schmutzig ). Die Daten an diesen Speicherorten werden in den Hauptspeicher zurückgeschrieben, wenn diese Daten aus dem Cache entfernt werden. Aus diesem Grund wird oft ein Fehlschlag in einem Write-Back-Cache benötigt zwei Speicherzugriffe zum Dienst: einer zum Lesen des neuen Ortes aus dem Speicher und der andere zum Schreiben des schmutzigen Ortes in den Speicher.

Es gibt auch Zwischenrichtlinien. Der Cache kann ein Write-Through sein, aber die Schreibvorgänge können vorübergehend in einer Speicherdatenwarteschlange gehalten werden, normalerweise so, dass mehrere Speicher zusammen verarbeitet werden können (was die Busumläufe reduzieren und so die Busauslastung verbessern kann).

Die zwischengespeicherten Daten im Hauptspeicher können von anderen Entitäten geändert werden, wodurch die Kopie im Cache veraltet sein kann oder abgestanden . Wenn die CPU alternativ die Daten im Cache aktualisiert, werden Kopien von Daten in anderen Caches veraltet. Kommunikationsprotokolle zwischen den Cache-Managern, die die Daten konsistent halten, werden als bezeichnet Kohärenzprotokolle .

Die Zeit, die benötigt wird, um ein Datum aus dem Speicher abzurufen (Leselatenz), ist von Bedeutung, da einer CPU beim Warten auf das Datum oft die Arbeit ausgeht. Wenn eine CPU diesen Zustand erreicht, wird dies als a bezeichnet Stall . Wenn CPUs schneller werden, verdrängen Verzögerungen aufgrund von Cache-Fehlschlägen mehr potenzielle Berechnungen; Moderne CPUs können Hunderte von Anweisungen in der Zeit ausführen, die zum Abrufen eines einzelnen Datums aus dem Speicher benötigt wird. Es wurden verschiedene Techniken eingesetzt, um die CPU während dieser Zeit beschäftigt zu halten. Außer Betrieb CPUs (z. B. Pentium Pro und spätere Intel-Designs) versuchen, unabhängige Befehle nach dem Befehl auszuführen, der auf die Cache-Fehltrefferdaten wartet. Der Pentium 4 verwendet simultanes Multithreading (Hyper-Threading in Intels Terminologie), um einem zweiten Programm zu ermöglichen, die CPU zu verwenden, während ein erstes Programm darauf wartet, dass Daten aus dem Hauptspeicher kommen.

Assoziativität

  Welche Speicherorte können von welchen Cache-Orten zwischengespeichert werden   Vergrößern Welche Speicherorte können von welchen Cache-Orten zwischengespeichert werden

Erinnern Sie sich, dass die Ersetzungsrichtlinie entscheidet, wo im Cache eine Kopie eines bestimmten Eintrags des Hauptspeichers abgelegt wird. Wenn die Ersetzungsrichtlinie frei ist, einen beliebigen Eintrag im Cache auszuwählen, um die Kopie zu halten, wird der Cache aufgerufen voll assoziativ . Das andere Extrem, wenn jeder Eintrag im Hauptspeicher an nur einer Stelle im Cache abgelegt werden kann, ist der Cache direkt abgebildet . Viele Caches implementieren einen Kompromiss und werden als beschrieben assoziativ setzen . Beispielsweise ist der Level-1-Daten-Cache in einem AMD Athlon 2-Wege-satzassoziativ, was bedeutet, dass jede bestimmte Stelle im Hauptspeicher an einer von 2 Stellen im Level-1-Daten-Cache zwischengespeichert werden kann.

Wenn jeder Ort im Hauptspeicher an einem von zwei Orten im Cache zwischengespeichert werden kann, lautet eine logische Frage: welche zwei? Das einfachste und am häufigsten verwendete Schema, das im obigen rechten Diagramm gezeigt wird, besteht darin, die niedrigstwertigen Bits des Index der Speicherstelle als Index für den Cache-Speicher zu verwenden und zwei Einträge für jeden Index zu haben. Eine gute Eigenschaft dieses Schemas besteht darin, dass die im Cache gespeicherten Tags nicht den Teil der Hauptspeicheradresse enthalten müssen, der durch den Index des Cache-Speichers spezifiziert ist. Da die Cache-Tags aus weniger Bits bestehen, nehmen sie weniger Platz ein und können schneller gelesen und verglichen werden.

Andere Schemata wurden vorgeschlagen, wie z verzerrter Cache , wobei der Index für Weg 0 wie oben direkt ist, aber der Index für Weg 1 mit einer Hash-Funktion gebildet wird. Eine gute Hash-Funktion hat die Eigenschaft, dass Adressen, die mit der direkten Abbildung in Konflikt stehen, tendenziell nicht zu Konflikten führen, wenn sie mit der Hash-Funktion abgebildet werden, und daher ist es weniger wahrscheinlich, dass ein Programm unter einer unerwartet großen Anzahl von Konfliktfehlern aufgrund eines pathologischen Zugriffs leidet Muster. Der Nachteil ist die zusätzliche Latenz bei der Berechnung der Hash-Funktion. Wenn es an der Zeit ist, eine neue Zeile zu laden und eine alte Zeile zu entfernen, kann es außerdem schwierig sein, festzustellen, welche vorhandene Zeile am längsten nicht verwendet wurde, da die neue Zeile in jeder Hinsicht mit Daten an unterschiedlichen Indizes kollidiert; Die LRU-Verfolgung für nicht schiefe Caches erfolgt normalerweise pro Satz.

Assoziativität ist ein Kompromiss. Wenn es zehn Orte gibt, kann die Ersetzungsrichtlinie einen neuen Cache-Eintrag setzen, dann müssen, wenn der Cache auf einen Treffer überprüft wird, alle zehn Orte durchsucht werden. Das Überprüfen von mehr Orten erfordert mehr Kraft, Fläche und möglicherweise Zeit. Andererseits erleiden Caches mit mehr Assoziativität weniger Fehler (siehe Konfliktfehler unten). Als Faustregel gilt, dass eine Verdoppelung der Assoziativität, von direkt abgebildet auf 2-Wege oder von 2-Wege auf 4-Wege, ungefähr die gleiche Auswirkung auf die Trefferrate hat wie eine Verdoppelung der Cache-Größe. Assoziativitätserhöhungen über 4-Wege hinaus haben viel weniger Einfluss auf die Trefferquote und werden im Allgemeinen aus anderen Gründen durchgeführt (siehe virtuelles Aliasing unten).

Einer der Vorteile eines direkt abgebildeten Caches besteht darin, dass er eine einfache und schnelle Spekulation ermöglicht. Sobald die Adresse berechnet wurde, ist der eine Cache-Index bekannt, der eine Kopie dieses Datums haben könnte. Dieser Cache-Eintrag kann gelesen werden, und der Prozessor kann mit diesen Daten weiterarbeiten, bevor er die Überprüfung beendet, ob das Tag tatsächlich mit der angeforderten Adresse übereinstimmt.

Die Idee, den Prozessor die zwischengespeicherten Daten verwenden zu lassen, bevor die Tag-Übereinstimmung abgeschlossen ist, kann auch auf assoziative Caches angewendet werden. Eine Teilmenge des Tags, genannt a Hinweis , kann verwendet werden, um nur einen der möglichen Cache-Einträge auszuwählen, die der angeforderten Adresse zugeordnet sind. Dieses Datum kann dann parallel zur Überprüfung des vollständigen Tags verwendet werden. Die Hinweistechnik funktioniert am besten, wenn sie im Zusammenhang mit der Adressübersetzung verwendet wird, wie unten erläutert.

Cache-Fehlschläge

Ein Cache-Miss bezieht sich auf einen fehlgeschlagenen Versuch, ein Datenelement im Cache zu lesen oder zu schreiben, was zu einem Hauptspeicherzugriff mit viel längerer Latenz führt. Es gibt drei Arten von Cache-Fehlschlägen.

Ein Cache-Lesefehler aus einem Befehls-Cache verursacht im Allgemeinen die größte Verzögerung, da der Prozessor oder zumindest der Ausführungs-Thread warten (anhalten) muss, bis der Befehl aus dem Hauptspeicher geholt wird.

Ein Cache-Lesefehler aus einem Daten-Cache verursacht normalerweise weniger Verzögerung, da Befehle, die nicht vom Cache-Lesen abhängig sind, ausgegeben werden und die Ausführung fortsetzen können, bis die Daten vom Hauptspeicher zurückgegeben werden, und die abhängigen Befehle die Ausführung wieder aufnehmen können.

Ein Cache-Schreibfehler in einen Daten-Cache verursacht im Allgemeinen die geringste Verzögerung, da das Schreiben in eine Warteschlange eingereiht werden kann und es wenige Einschränkungen bei der Ausführung nachfolgender Befehle gibt. Der Prozessor kann fortfahren, bis die Warteschlange voll ist.

Um die Cache-Miss-Rate zu senken, wurde das Cache-Verhalten ausführlich analysiert, um die beste Kombination aus Größe, Assoziativität, Blockgröße usw. zu finden. Sequenzen von Speicherreferenzen, die von Benchmark-Programmen durchgeführt werden, werden gespeichert als Adressspuren . Nachfolgende Analysen simulieren viele verschiedene mögliche Cache-Designs auf diesen langen Adressspuren. Es kann ziemlich verwirrend sein, zu verstehen, wie sich die vielen Variablen auf die Cache-Trefferrate auswirken. Ein wesentlicher Beitrag zu dieser Analyse wurde von Mark Hill geleistet, der Fehlschläge in drei Kategorien einteilte (bekannt als die drei Cs):

  • Pflichtfehler sind die Fehler, die durch die erste Referenz auf ein Datum verursacht werden. Cache-Größe und Assoziativität haben keinen Einfluss auf die Anzahl der Zwangsfehler. Prefetching kann hier helfen, ebenso wie größere Cache-Blockgrößen (die eine Form des Prefetching sind).
  • Kapazität fehlt sind jene Misses, die unabhängig von Assoziativität oder Blockgröße allein aufgrund der endlichen Größe des Caches auftreten. Die Kurve der Kapazitätsfehlrate gegenüber der Cache-Größe gibt ein gewisses Maß für die zeitliche Lokalität eines bestimmten Referenzstroms. Beachten Sie, dass es keine sinnvolle Vorstellung davon gibt, dass ein Cache 'voll' oder 'leer' oder 'fast ausgelastet' ist: CPU-Caches haben fast immer fast jede Zeile mit einer Kopie einer Zeile im Hauptspeicher gefüllt und fast jede Zuweisung einer neuen Leitung erfordert die Räumung einer alten Leitung.
  • Konflikt verfehlt sind die Fehler, die hätten vermieden werden können, wenn der Cache nicht früher einen Eintrag entfernt hätte. Konfliktfehler können weiter unterteilt werden in Zuordnung fehlt , die bei einem bestimmten Maß an Assoziativität unvermeidbar sind, und Ersatz fehlt , die auf die besondere Opferwahl der Ersatzpolice zurückzuführen sind.
  Miss-Rate versus Cache-Größe auf dem Integer-Teil von SPEC CPU2000   Vergrößern Miss-Rate versus Cache-Größe auf dem Integer-Teil von SPEC CPU2000

Das Diagramm auf der rechten Seite fasst die Cache-Leistung zusammen, die im Integer-Teil der SPEC CPU2000-Benchmarks gesehen wird, wie sie von Hill und Cantin gesammelt wurden. Diese Benchmarks sollen die Art der Arbeitsbelastung darstellen, die ein Engineering-Workstation-Computer an einem bestimmten Tag sehen kann. Wir können die unterschiedlichen Wirkungen der drei Cs in diesem Diagramm sehen.

Ganz rechts, mit der Cache-Größe, die mit 'Inf' gekennzeichnet ist, haben wir die obligatorischen Fehler. Wenn wir die Leistung einer Maschine auf SpecInt2000 verbessern möchten, ist eine Erhöhung der Cache-Größe über 1 MiB im Wesentlichen zwecklos. Das ist die Erkenntnis, die die Pflichtfehler vermitteln.

Die vollassoziative Cache-Fehlschlagrate ist hier fast repräsentativ für die Kapazitäts-Fehlschlagrate. Der Unterschied besteht darin, dass die präsentierten Daten aus Simulationen stammen, die eine LRU-Ersatzpolitik annehmen. Das Anzeigen der Kapazitätsfehlrate würde eine perfekte Ersetzungspolitik erfordern, d. h. ein Orakel, das in die Zukunft schaut, um einen Cache-Eintrag zu finden, der tatsächlich nicht getroffen wird.

Beachten Sie, dass unsere Annäherung der Kapazitätsfehlrate zwischen 32 KiB und 64 KiB steil abfällt. Dies zeigt an, dass der Benchmark a hat Workingset von etwa 64 KiB. Ein CPU-Cache-Designer, der diesen Benchmark untersucht, wird einen starken Anreiz haben, die Cache-Größe auf 64 KiB statt auf 32 KiB festzulegen. Beachten Sie, dass bei diesem Benchmark kein Grad an Assoziativität dazu führen kann, dass ein 32-KiB-Cache so gut funktioniert wie ein 64-KiB-4-Wege- oder sogar ein direkt zugeordneter 128-KiB-Cache.

Beachten Sie schließlich, dass zwischen 64 KiB und 1 MiB ein großer Unterschied zwischen direkt zugeordneten und vollständig assoziativen Caches besteht. Diese Differenz ist die Konfliktverfehlungsrate. Seit 2004 liegen On-Chip-Sekundär-Caches tendenziell in diesem Bereich, da kleinere Caches schnell genug sind, um Primär-Caches zu sein, und größere Caches zu groß werden, um wirtschaftlich On-Chip zu produzieren (Itanium 2 hat 9 MiB Level-3 auf -Chip-Cache, der größte Versand-On-Chip-Cache im Jahr 2004). Die Erkenntnis aus der Betrachtung der Konfliktfehlraten ist, dass sekundäre Caches stark von einer hohen Assoziativität profitieren.

Dieser Vorteil war Ende der 80er und Anfang der 90er Jahre bekannt, als CPU-Designer keine großen Caches auf dem Chip einbauen konnten und weder dem Cache-Datenspeicher noch dem Cache-Tag-Speicher genügend Bandbreite zur Verfügung stellen konnten, um eine hohe Assoziativität in Off-Chip-Caches zu implementieren . Verzweifelte Hacks wurden versucht: Der MIPS R8000 verwendete teure Off-Chip-dedizierte Tag-SRAMs mit eingebetteten Tag-Komparatoren und großen Treibern auf den Übereinstimmungsleitungen, um einen 4-MiB-4-Wege-Assoziativ-Cache zu implementieren. Der MIPS R10000 verwendete gewöhnliche SRAM-Chips für die Tags. Der Tag-Zugriff für beide Wege dauerte zwei Zyklen. Um die Latenz zu reduzieren, würde der R10000 erraten, welche Richtung des Caches bei jedem Zugriff getroffen wird.

Adressübersetzung

Die meisten Allzweck-CPUs implementieren irgendeine Form von virtuellem Speicher. Zusammenfassend sieht jedes Programm, das auf der Maschine läuft, seinen eigenen vereinfachten Adressraum, der nur Code und Daten für dieses Programm enthält. Jedes Programm platziert Dinge in seinem Adressraum ohne Rücksicht darauf, was andere Programme in ihren Adressräumen tun.

Der virtuelle Speicher erfordert, dass der Prozessor vom Programm generierte virtuelle Adressen in physische Adressen im Hauptspeicher übersetzt. Der Teil des Prozessors, der diese Übersetzung durchführt, ist als Memory Management Unit (MMU) bekannt. Der schnelle Pfad durch die MMU kann die Übersetzungen ausführen, die im umständlich benannten Translation Lookaside Buffer (TLB) gespeichert sind, der ein Cache für Zuordnungen aus der Seitentabelle des Betriebssystems ist.

Für die Zwecke der vorliegenden Diskussion gibt es drei wichtige Merkmale der Adressübersetzung:

  • Latenz: Die physische Adresse ist von der MMU einige Zeit verfügbar, vielleicht ein paar Zyklen, nachdem die virtuelle Adresse von dem Adressgenerator verfügbar ist.
  • Aliasing: Mehrere virtuelle Adressen können einer einzigen physischen Adresse zugeordnet werden. Die meisten Prozessoren garantieren, dass alle Aktualisierungen an dieser einzelnen physischen Adresse in der Programmreihenfolge erfolgen. Um diese Garantie zu erfüllen, muss der Prozessor sicherstellen, dass sich zu einem bestimmten Zeitpunkt nur eine Kopie einer physischen Adresse im Cache befindet.
  • Die Granularität: Der virtuelle Adressraum ist in Seiten aufgeteilt. Beispielsweise könnte ein virtueller Adressraum von 4 GiB in 1048576 4-KiB-Seiten aufgeteilt werden, von denen jede unabhängig zugeordnet werden kann. Es können mehrere Seitengrößen unterstützt werden, siehe virtueller Speicher für eine ausführlichere Beschreibung.

Eine historische Anmerkung: Die ersten virtuellen Speichersysteme waren sehr langsam, da sie vor jedem programmierten Zugriff auf den Hauptspeicher einen Zugriff auf die (im Hauptspeicher gehaltene) Seitentabelle erforderten. Ohne Caches reduziert dies die Geschwindigkeit der Maschine effektiv um die Hälfte. Der erste Hardware-Cache, der in einem Computersystem verwendet wurde, war eigentlich kein Daten- oder Befehlscache, sondern eher ein TLB.

Die Existenz unterschiedlicher physikalischer und virtueller Adressen wirft die Frage auf, ob virtuelle oder physikalische Adressen für den Cache-Index und das Tag verwendet werden. Die Motivation für die Verwendung virtueller Adressen ist die Geschwindigkeit: Ein virtuell indizierter, virtuell gekennzeichneter Datencache schneidet die MMU vollständig aus der Last-Verwendungs-Wiederholung heraus. Die Latenz dieser Wiederholung ( Latenz laden ) ist entscheidend für die Leistung einer CPU. Die meisten modernen Level-1-Caches sind virtuell indiziert, wodurch zumindest die TLB-Suche der MMU parallel zum Abrufen der Daten aus dem Cache-RAM fortgesetzt werden kann.

Aber die virtuelle Indizierung ist nicht immer die beste Wahl. Es führt zu dem Problem virtueller Aliase – der Cache kann mehrere Speicherorte haben, die den Wert einer einzelnen physikalischen Adresse speichern können. Die Kosten für den Umgang mit virtuellen Aliasnamen wachsen mit der Cache-Größe, und als Ergebnis werden die meisten Level-2- und größeren Caches physisch indiziert.

Virtuelles Tagging ist ungewöhnlich. Wenn die TLB-Suche vor der Cache-RAM-Suche beendet werden kann, ist die physische Adresse rechtzeitig für den Tag-Vergleich verfügbar, und es besteht keine Notwendigkeit für virtuelles Tagging. Große Caches werden dann tendenziell physisch markiert, und nur kleine Caches mit sehr geringer Latenz werden virtuell markiert. In neueren Allzweck-CPUs wurde das virtuelle Tagging durch Vhints ersetzt, wie unten beschrieben.

Virtuelle Indizierung und virtuelle Aliase

Die übliche Art und Weise, wie der Prozessor garantiert, dass virtuelle Alias-Adressen als ein einziger Speicherort fungieren, besteht darin, dafür zu sorgen, dass sich zu einem bestimmten Zeitpunkt nur ein virtueller Alias ​​im Cache befinden kann.

Immer wenn ein neuer Eintrag zu einem virtuell indizierten Cache hinzugefügt wird, sucht der Prozessor nach irgendwelchen bereits vorhandenen virtuellen Aliasen und entfernt sie zuerst. Diese spezielle Behandlung findet nur während eines Cache-Fehltreffers statt. Während eines Cache-Treffers ist keine besondere Arbeit erforderlich, was dazu beiträgt, den schnellen Pfad schnell zu halten.

Der einfachste Weg, Aliase zu finden, besteht darin, dafür zu sorgen, dass sie alle demselben Ort im Cache zugeordnet werden. Dies geschieht beispielsweise, wenn der TLB z. 4 KiB-Seiten, und der Cache ist direkt zugeordnet und 4 KiB oder weniger.

Moderne Level-1-Caches sind viel größer als 4 KiB, aber virtuelle Speicherseiten haben diese Größe beibehalten. Ist der Cache z.B. 16 KiB und virtuell indiziert, gibt es für jede virtuelle Adresse vier Cache-Speicherorte, die denselben physischen Speicherort enthalten könnten, aber mit unterschiedlichen virtuellen Adressen aliasiert sind. Wenn der Cache fehlschlägt, müssen alle vier Speicherorte sondiert werden, um zu sehen, ob ihre entsprechenden physikalischen Adressen mit der physikalischen Adresse des Zugriffs übereinstimmen, der den Fehlschlag erzeugt hat.

Diese Sonden sind die gleichen Prüfungen, die ein satzassoziativer Cache verwendet, um eine bestimmte Übereinstimmung auszuwählen. Wenn also ein virtuell indizierter 16-KiB-Cache 4-fach satzassoziativ ist und mit virtuellen Speicherseiten von 4 KiB verwendet wird, ist keine besondere Arbeit erforderlich, um virtuelle Aliase während Cache-Fehlschlägen zu entfernen, da die Überprüfungen bereits durchgeführt wurden, während auf einen Cache-Treffer geprüft wurde.

Um den AMD Athlon erneut als Beispiel zu verwenden, verfügt er über einen 64-KiB-Level-1-Datencache, 4-KiB-Seiten und eine 2-Wege-Set-Assoziativität. Wenn der Level-1-Daten-Cache einen Fehler erleidet, wurden 2 der 16 (== 64 KiB/4 KiB) möglichen virtuellen Aliase bereits geprüft, und sieben weitere Zyklen durch die Tag-Prüfhardware sind erforderlich, um die Prüfung auf virtuelle Aliase abzuschließen .

Virtuelle Tags und Vhints

Auch virtuelles Tagging ist möglich. Der große Vorteil virtueller Tags besteht darin, dass sie bei assoziativen Caches ermöglichen, dass der Tag-Abgleich fortgesetzt wird, bevor die virtuelle in physische Übersetzung erfolgt ist. Jedoch,

  • Kohärenzuntersuchungen und Räumungen bieten eine physische Adresse zum Handeln. Die Hardware muss über Mittel zum Umwandeln der physikalischen Adressen in einen Cache-Index verfügen, im Allgemeinen durch Speichern physikalischer Tags sowie virtueller Tags. Zum Vergleich: Ein Cache mit physikalischen Tags muss keine virtuellen Tags behalten, was einfacher ist.
  • Wenn eine Zuordnung von virtuell zu physisch aus dem TLB gelöscht wird, müssen Cache-Einträge mit diesen virtuellen Adressen irgendwie geleert werden. Alternativ müssen, wenn Cache-Einträge auf Seiten erlaubt sind, die nicht vom TLB abgebildet werden, diese Einträge geleert werden, wenn die Zugriffsrechte auf diesen Seiten in der Seitentabelle geändert werden.

Es ist auch möglich, dass das Betriebssystem sicherstellt, dass keine virtuellen Aliase gleichzeitig im Cache resident sind. Das Betriebssystem leistet diese Garantie, indem es die Seitenfärbung erzwingt, die unten beschrieben wird. Einige frühe RISC-Prozessoren (SPARC, RS/6000) haben diesen Ansatz gewählt. Es wurde in letzter Zeit nicht verwendet, da die Hardwarekosten zum Erkennen und Entfernen virtueller Aliase gesunken sind und die Softwarekomplexität und die Leistungseinbußen bei der perfekten Seitenfärbung gestiegen sind.

Es kann nützlich sein, die beiden Funktionen von Tags in einem assoziativen Cache zu unterscheiden: Sie werden verwendet, um zu bestimmen, welcher Weg des Eintragssatzes ausgewählt werden soll, und sie werden verwendet, um zu bestimmen, ob der Cache getroffen oder verfehlt wurde. Die zweite Funktion muss immer richtig sein, aber es ist zulässig, dass die erste Funktion rät und gelegentlich die falsche Antwort erhält.

Einige Prozessoren (z. B. frühe SPARCs) haben Caches mit virtuellen und physischen Tags. Die virtuellen Tags werden zur Wegauswahl verwendet, und die physischen Tags werden zum Bestimmen von Treffern oder Fehlschlägen verwendet. Diese Art von Cache genießt den Latenzvorteil eines virtuell gekennzeichneten Caches und die einfache Softwareschnittstelle eines physisch gekennzeichneten Caches. Es trägt jedoch die zusätzlichen Kosten für duplizierte Tags. Außerdem müssen während der Fehlschlagverarbeitung die alternativen Wege der indizierten Cache-Zeile auf virtuelle Aliase abgetastet und jegliche Übereinstimmungen entfernt werden.

Der zusätzliche Bereich (und etwas Latenz) kann durch Beibehalten gemildert werden virtuelle Hinweise mit jedem Cache-Eintrag anstelle von virtuellen Tags. Diese Hinweise sind eine Teilmenge oder ein Hash des virtuellen Tags und werden verwendet, um den Weg des Caches auszuwählen, von dem Daten und ein physisches Tag abgerufen werden sollen. Wie bei einem virtuell markierten Cache kann es eine virtuelle Hinweisübereinstimmung, aber eine physische Tag-Nichtübereinstimmung geben, in diesem Fall muss der Cache-Eintrag mit dem übereinstimmenden Hinweis entfernt werden, so dass Cache-Zugriffe nach dem Füllen des Caches an dieser Adresse nur eine Hinweisübereinstimmung aufweisen. Da virtuelle Hinweise weniger Bits als virtuelle Tags haben, die sie voneinander unterscheiden, erleidet ein virtuell angedeuteter Cache mehr Konfliktfehler als ein virtuell gekennzeichneter Cache.

Die vielleicht ultimative Reduzierung virtueller Hinweise findet sich im Pentium 4 (Willamette- und Northwood-Kerne). Bei diesen Prozessoren beträgt der virtuelle Hinweis effektiv 2 Bits, und der Cache ist 4-fach satzassoziativ. Tatsächlich behält die Hardware eine einfache Permutation von der virtuellen Adresse zum Cache-Index bei, so dass kein CAM notwendig ist, um den richtigen der vier abgerufenen Wege auszuwählen.

Seitenfärbung

Große physisch indizierte Caches (normalerweise sekundäre Caches) stoßen auf ein Problem: Das Betriebssystem und nicht die Anwendung steuert, welche Seiten im Cache miteinander kollidieren. Unterschiede in der Seitenzuordnung von einem Programmlauf zum nächsten führen zu Unterschieden in den Cache-Kollisionsmustern, was zu sehr großen Unterschieden in der Programmleistung führen kann. Diese Unterschiede können es sehr schwierig machen, ein konsistentes und wiederholbares Timing für einen Benchmark-Durchlauf zu erhalten, was dann zu frustrierten Vertriebsingenieuren führt, die verlangen, dass die Betriebssystemautoren das Problem beheben.

Um das Problem zu verstehen, stellen Sie sich eine CPU mit einem physisch indizierten, direkt zugeordneten Level-2-Cache von 1 MiB und virtuellen Speicherseiten von 4 KiB vor. Sequentielle physische Seiten werden sequentiellen Stellen im Cache zugeordnet, bis das Muster nach 256 Seiten umläuft. Wir können jede physische Seite mit einer Farbe von 0-255 kennzeichnen, um anzugeben, wohin sie im Cache gehen kann. Speicherorte innerhalb physischer Seiten mit unterschiedlichen Farben können im Cache nicht in Konflikt geraten.

Ein Programmierer, der versucht, den Cache maximal zu nutzen, kann die Zugriffsmuster seines Programms so anordnen, dass zu jedem Zeitpunkt nur 1 MiB Daten zwischengespeichert werden müssen, wodurch Kapazitätsverluste vermieden werden. Aber er sollte auch sicherstellen, dass die Zugriffsmuster keine Konfliktfehler aufweisen. Eine Möglichkeit, dieses Problem zu lösen, besteht darin, die virtuellen Seiten, die das Programm verwendet, aufzuteilen und ihnen virtuelle Farben auf die gleiche Weise zuzuweisen, wie zuvor physischen Seiten physische Farben zugewiesen wurden. Der Programmierer kann dann die Zugriffsmuster seines Codes so gestalten, dass nicht zwei Seiten mit derselben virtuellen Farbe gleichzeitig verwendet werden. Es gibt eine umfangreiche Literatur zu solchen Optimierungen (z. B. Schleifenverschachtelungsoptimierung), die größtenteils aus der High Performance Computing (HPC)-Community stammt.

Der Haken ist, dass zwar alle Seiten, die zu einem bestimmten Zeitpunkt verwendet werden, unterschiedliche virtuelle Farben haben können, einige jedoch dieselben physischen Farben haben können. Wenn das Betriebssystem physische Seiten zufällig und einheitlich virtuellen Seiten zuweist, ist es tatsächlich sehr wahrscheinlich, dass einige Seiten dieselbe physische Farbe haben und dann Orte dieser Seiten im Cache kollidieren (das ist das Geburtstagsparadoxon).

Die Lösung besteht darin, das Betriebssystem versuchen zu lassen, unterschiedliche physische Farbseiten unterschiedlichen virtuellen Farben zuzuweisen, eine Technik, die als Seite färben . Obwohl die tatsächliche Zuordnung von virtueller zu physischer Farbe für die Systemleistung irrelevant ist, sind ungerade Zuordnungen schwer zu verfolgen und haben wenig Nutzen, sodass die meisten Ansätze zur Seitenfärbung einfach versuchen, physische und virtuelle Seitenfarben gleich zu halten.

Wenn das Betriebssystem garantieren kann, dass jede physische Seite nur auf eine virtuelle Farbe abgebildet wird, dann gibt es keine virtuellen Aliase, und der Prozessor kann virtuell indizierte Caches verwenden, ohne dass zusätzliche virtuelle Alias-Prüfungen während der Fehlerbehandlung erforderlich sind. Alternativ kann das O/S eine Seite immer dann aus dem Cache löschen, wenn sie von einer virtuellen Farbe zu einer anderen wechselt. Wie oben erwähnt, wurde dieser Ansatz für einige frühe SPARC- und RS/6000-Designs verwendet.

Cache-Hierarchie in einem modernen Prozessor

Moderne Prozessoren haben mehrere interagierende Caches auf dem Chip. Zwei Probleme haben die Entwicklung der modernen Cache-Hierarchie vorangetrieben.

Spezialisierte Caches

Das erste Problem besteht darin, dass Pipeline-CPUs von mehreren Punkten in der Pipeline aus auf den Speicher zugreifen: Befehlsabruf, Übersetzung von virtuellen in physische Adressen und Datenabruf. Ein einfaches Beispiel finden Sie unter Klassische RISC-Pipeline. Das natürliche Design besteht darin, für jeden dieser Punkte unterschiedliche physische Caches zu verwenden, sodass keine physische Ressource eingeplant werden muss, um zwei Punkte in der Pipeline zu bedienen. Somit endet die Pipeline natürlich mit mindestens drei getrennten Caches (Befehl, TLB und Daten), die jeweils auf ihre spezielle Rolle spezialisiert sind.

Opfercache

EIN Opfercache ist ein Cache, der verwendet wird, um Blöcke zu halten, die aufgrund eines Konflikts oder eines Kapazitätsfehlers aus einem CPU-Cache entfernt wurden. Der Opfer-Cache liegt zwischen dem Haupt-Cache und seinem Nachfüllpfad und enthält nur Blöcke, die bei einem Fehlschlag aus diesem Cache entfernt wurden. Diese Technik wird verwendet, um die Strafe zu reduzieren, die einem Cache bei einem Fehltreffer zugefügt wird.

Der ursprüngliche Opfer-Cache auf dem HP PA7200 war ein kleiner, vollständig assoziativer Cache. Spätere Prozessoren wie AMD K7 und K8 verwendeten den sehr großen Sekundärcache als Opfercache, um eine doppelte Speicherung des Inhalts des großen Primärcaches zu vermeiden.

Trace-Cache

Eines der extremeren Beispiele für Cache-Spezialisierung ist die Trace-Cache gefunden in den Intel Pentium 4 Mikroprozessoren. EIN Trace-Cache ist ein Mechanismus zum Erhöhen der Befehlsabrufbandbreite und zum Senken des Stromverbrauchs (im Fall des Pentium 4) durch Speichern von Spuren von Befehlen, die bereits abgerufen und dekodiert wurden. Der Mechanismus wurde erstmals von Eric Rotenberg, Steve Bennett und Jim Smith in ihrer Arbeit von 1996 vorgeschlagen 'Trace-Cache: ein Ansatz mit geringer Latenz zum Abrufen von Anweisungen mit hoher Bandbreite.'

Ein Trace-Cache speichert Anweisungen entweder nachdem sie dekodiert wurden oder wenn sie zurückgezogen werden. Im Allgemeinen werden Anweisungen zu Trace-Caches in Gruppen hinzugefügt, die entweder einzelne Basisblöcke oder dynamische Anweisungs-Traces darstellen. Ein Basisblock besteht aus einer Gruppe von Nicht-Verzweigungsbefehlen, die mit einer Verzweigung enden. Ein dynamischer Trace ('Trace-Pfad') enthält nur Anweisungen, deren Ergebnisse tatsächlich verwendet werden, und eliminiert Anweisungen nach genommenen Verzweigungen (da sie nicht ausgeführt werden); ein dynamischer Trace kann eine Verkettung mehrerer Basisblöcke sein. Dadurch kann die Befehlsabrufeinheit eines Prozessors mehrere Basisblöcke abrufen, ohne sich um Verzweigungen im Ausführungsablauf kümmern zu müssen.

Ablaufverfolgungszeilen werden basierend auf dem Programmzähler der ersten Anweisung in der Ablaufverfolgung und einem Satz von Verzweigungsvorhersagen im Ablaufverfolgungscache gespeichert. Dies ermöglicht das Speichern verschiedener Ablaufverfolgungspfade, die an derselben Adresse beginnen und jeweils unterschiedliche Verzweigungsergebnisse darstellen. In der Befehlsabrufstufe einer Pipeline wird der aktuelle Programmzähler zusammen mit einem Satz von Verzweigungsvorhersagen im Trace-Cache auf einen Treffer geprüft. Wenn es einen Treffer gibt, wird eine Trace-Zeile zum Abrufen bereitgestellt, die für diese Befehle nicht zu einem regulären Cache oder Speicher gehen muss. Der Ablaufverfolgungscache speist die Abrufeinheit weiter, bis die Ablaufverfolgungslinie endet oder bis eine Fehlvorhersage in der Pipeline auftritt. Wenn es einen Fehler gibt, beginnt eine neue Spur aufgebaut zu werden.

Trace-Caches werden auch in Prozessoren wie dem Intel Pentium 4 verwendet, um bereits dekodierte Mikrooperationen oder Übersetzungen komplexer x86-Anweisungen zu speichern, damit eine Anweisung, wenn sie das nächste Mal benötigt wird, nicht erneut dekodiert werden muss.

Siehe den vollständigen Text der Arbeit von Smith, Rotenberg und Bennett unter Zitterer .

Harvard-Architektur

Pipelines mit getrennten Befehls- und Datencaches sollen eine Harvard-Architektur haben. Ursprünglich bezog sich dieser Ausdruck auf Maschinen mit getrennten Befehls- und Datenspeichern, sodass es für ein Programm keine Möglichkeit gab, seine Befehle zu ändern.

Mehrstufige Caches

Das zweite Problem ist der grundlegende Kompromiss zwischen Cache-Latenz und Trefferrate. Größere Caches haben bessere Trefferquoten, aber längere Latenzzeiten. Um diesen Kompromiss zu verbessern, verwenden viele Computer mehrere Cache-Ebenen, wobei kleine schnelle Caches durch größere langsamere Caches gesichert werden. Da der Latenzunterschied zwischen dem Hauptspeicher und dem schnellsten Cache größer geworden ist, haben einige Prozessoren damit begonnen, bis zu drei Ebenen des On-Chip-Cache zu verwenden. Beispielsweise wurde Itanium II im Jahr 2003 mit einem 6 MiB einheitlichen Level-3-Cache auf dem Chip ausgeliefert. Die IBM Power 4-Serie verfügt über einen 256 MiB großen Level-3-Cache außerhalb des Chips, der von mehreren Prozessoren gemeinsam genutzt wird.

Caches mit mehreren Ebenen arbeiten im Allgemeinen, indem sie die kleinsten überprüfen Level 1 zuerst zwischenspeichern; Wenn es trifft, fährt der Prozessor mit hoher Geschwindigkeit fort. Wenn der kleinere Cache fehlschlägt, wird der nächstgrößere Cache überprüft und so weiter, bevor der Hauptspeicher überprüft wird.

Multi-Level-Caches führen neue Entwurfsentscheidungen ein. Beispielsweise können sich bei einigen Prozessoren (wie Intel Pentium 2, 3 und 4 sowie den meisten RISCs) die Daten im L1-Cache auch im L2-Cache befinden. Diese Caches werden aufgerufen inklusive . Andere Prozessoren (wie der AMD Athlon) haben exklusiv Caches – Daten befinden sich garantiert in höchstens einem der L1- und L2-Caches.

Der Vorteil von exklusiven Caches ist, dass sie mehr Daten speichern. Dieser Vorteil ist bei größeren Caches größer. Wenn L1 verfehlt und L2 einen Zugriff trifft, wird die treffende Cache-Zeile in L2 mit einer Zeile in L1 ausgetauscht. Dieser Austausch ist ziemlich viel mehr Arbeit, als nur eine Zeile von L2 nach L1 zu kopieren, was ein inklusiver Cache tut.

Einige Implementierungen inklusiver Caches garantieren, dass sich alle Daten im L1-Cache auch im L2-Cache befinden. Ein Vorteil streng inklusiver Caches besteht darin, dass externe Geräte oder andere Prozessoren in einem Mehrprozessorsystem, wenn sie eine Cache-Zeile aus dem Prozessor entfernen möchten, den Prozessor nur den L2-Cache überprüfen lassen müssen. In Cache-Hierarchien, die keine Einbeziehung erzwingen, muss auch der L1-Cache überprüft werden. Als Nachteil besteht eine Korrelation zwischen den Assoziativitäten von L1- und L2-Caches: Wenn der L2-Cache nicht mindestens so viele Wege hat wie alle L1-Caches zusammen, wird die effektive Assoziativität der L1-Caches eingeschränkt.

Ein weiterer Vorteil inklusiver Caches besteht darin, dass der größere Cache größere Cache-Zeilen verwenden kann, wodurch die Größe der sekundären Cache-Tags reduziert wird. Wenn der sekundäre Cache um eine Größenordnung größer als der primäre ist und die Cache-Daten um eine Größenordnung größer als die Cache-Tags sind, kann dieser eingesparte Tag-Bereich mit dem inkrementellen Bereich vergleichbar sein, der zum Speichern der L1-Cache-Daten im L2 benötigt wird .

Wie oben erwähnt, haben größere Computer manchmal einen weiteren Cache zwischen dem L2-Cache und dem Hauptspeicher, der als L3-Cache bezeichnet wird. Dieser Cache kann auf einem separaten Chip von der CPU implementiert werden und kann ab 2004 eine Größe von 2 bis 256 Megabyte haben. Die Implementierung eines Off-Chip-L3-Cache kostet im Allgemeinen weit über 1000 US-Dollar, und seine Vorteile hängen von den Zugriffsmustern der Anwendung ab. High-End-x86-Workstations und -Server sind jetzt mit einer L3-Cache-Option erhältlich, die auf dem Mikroprozessorchip implementiert ist, was die Geschwindigkeit erhöht und die Kosten erheblich senkt. Zum Beispiel verfügt Intels Xeon MP-Produkt mit dem Codenamen „Tulsa“ über 16 MiB On-Die-L3-Cache, der von zwei Prozessorkernen gemeinsam genutzt wird.

Am anderen Ende der Speicherhierarchie schließlich kann die CPU-Registerdatei selbst als kleinster und schnellster Cache im System betrachtet werden, mit der besonderen Eigenschaft, dass sie in Software geplant ist – typischerweise von einem Compiler, da sie Register zuweist Halten Sie Werte, die aus dem Hauptspeicher abgerufen werden. (Siehe insbesondere Schleifenverschachtelungsoptimierung.) Registerdateien haben manchmal auch eine Hierarchie: Der Cray-1 (ca. 1976) hatte 8 Adress-'A'- und 8 Skalardaten-'S'-Register, die allgemein verwendbar waren. Es gab auch einen Satz von 64 Adress-'B'- und 64 Skalardaten-'T'-Registern, deren Zugriff länger dauerte, aber schneller als der Hauptspeicher waren. Die Register 'B' und 'T' wurden bereitgestellt, weil der Cray-1 keinen Datencache hatte. (Der Cray-1 hatte jedoch einen Befehls-Cache.)

Beispiel: der K8

Um sowohl die Spezialisierung als auch das Multi-Level-Caching zu veranschaulichen, ist hier die Cache-Hierarchie des AMD Athlon 64, dessen Kerndesign als K8 bekannt ist.

  Beispiel für Hierarchie, die K8 Beispiel für Hierarchie, die K8

Der K8 hat 4 spezialisierte Caches: einen Instruktions-Cache, einen Instruktions-TLB, einen Daten-TLB und einen Daten-Cache. Jeder dieser Caches ist spezialisiert:

  • Der Befehls-Cache hält Kopien von 64-Byte-Speicherzeilen und ruft 16 Bytes in jedem Zyklus ab. Jedes Byte in diesem Cache wird in zehn Bits statt in 8 gespeichert, wobei die zusätzlichen Bits die Grenzen der Befehle markieren (dies ist ein Beispiel für Vordecodierung). Der Cache hat nur Paritätsschutz anstelle von ECC, da die Parität kleiner ist und beschädigte Daten durch frische Daten ersetzt werden können, die aus dem Speicher abgerufen werden (der immer eine aktuelle Kopie der Anweisungen enthält).
  • Der Befehl TLB behält Kopien von Seitentabelleneinträgen (PTEs). Beim Befehlsabruf jedes Zyklus wird seine virtuelle Adresse durch diesen TLB in eine physikalische Adresse übersetzt. Jeder Eintrag ist entweder 4 oder 8 Bytes im Speicher. Jeder der TLBs ist in zwei Abschnitte unterteilt, einen zum Aufbewahren von PTEs, die 4 KiB abbilden, und einen zum Aufbewahren von PTEs, die 4 MiB oder 2 MiB abbilden. Die Teilung ermöglicht eine einfachere vollständig assoziative Anpassungsschaltung in jedem Abschnitt. Das Betriebssystem bildet verschiedene Abschnitte des virtuellen Adressraums mit unterschiedlich großen PTEs ab.
  • Der Daten-TLB hat zwei Kopien, die identische Einträge behalten. Die zwei Kopien ermöglichen zwei Datenzugriffe pro Zyklus, um virtuelle Adressen in physikalische Adressen zu übersetzen. Wie der Anweisungs-TLB ist dieser TLB in zwei Arten von Einträgen aufgeteilt.
  • Der Datencache behält Kopien von 64 Byte Speicherzeilen. Es ist in 8 Bänke aufgeteilt (wobei jede 8 KiB Daten speichert) und kann in jedem Zyklus zwei 8-Byte-Daten abrufen, solange sich diese Daten in verschiedenen Bänken befinden. Es gibt zwei Kopien der Tags, da jede 64-Byte-Zeile auf alle 8 Bänke verteilt ist. Jede Tag-Kopie handhabt einen der zwei Zugriffe pro Zyklus.

Der K8 hat auch mehrstufige Caches. Es gibt Befehls- und Daten-TLBs der zweiten Ebene, die nur PTEs speichern, die 4 KiB abbilden. Sowohl Befehls- als auch Daten-Caches und die verschiedenen TLBs können aus dem Großen gefüllt werden einheitlich L2-Cache. Dieser Cache ist sowohl für den L1-Befehls- als auch den Daten-Cache exklusiv, was bedeutet, dass sich jede 8-Byte-Zeile nur im L1-Befehls-Cache, im L1-Daten-Cache oder im L2-Cache befinden kann. Es ist jedoch möglich, dass eine Zeile im Datencache einen PTE hat, der sich auch in einem der TLBs befindet – das Betriebssystem ist dafür verantwortlich, die TLBs kohärent zu halten, indem Teile davon geleert werden, wenn die Seitentabellen im Speicher aktualisiert werden.

Der K8 speichert auch Informationen, die niemals im Speicher gespeichert werden – Vorhersageinformationen. Diese Caches sind im obigen Diagramm nicht dargestellt. Wie es für diese CPU-Klasse üblich ist, verfügt der K8 über eine ziemlich komplexe Verzweigungsvorhersage mit Tabellen, die helfen vorherzusagen, ob Verzweigungen genommen werden, und anderen Tabellen, die die Ziele von Verzweigungen und Sprüngen vorhersagen. Einige dieser Informationen sind Befehlen sowohl im Befehls-Cache der Ebene 1 als auch im vereinheitlichten sekundären Cache zugeordnet.

Der K8 verwendet einen interessanten Trick, um Vorhersageinformationen mit Anweisungen im sekundären Cache zu speichern. Zeilen im sekundären Cache werden vor versehentlicher Datenverfälschung (z. B. durch einen Alpha-Partikel-Strike) entweder durch ECC oder Parität geschützt, je nachdem, ob diese Zeilen aus den primären Daten- oder Befehls-Caches entfernt wurden. Da der Paritätscode weniger Bits benötigt als der ECC-Code, haben Zeilen aus dem Befehls-Cache ein paar freie Bits. Diese Bits werden verwendet, um Verzweigungsvorhersageinformationen, die diesen Befehlen zugeordnet sind, zwischenzuspeichern. Das Nettoergebnis besteht darin, dass der Verzweigungsprädiktor eine größere effektive Verlaufstabelle und somit eine bessere Genauigkeit aufweist.

Mehr Hierarchien

Andere Prozessoren haben andere Arten von Prädiktoren (z. B. den Store-to-Load-Bypass-Prädiktor im DEC Alpha 21264), und verschiedene spezialisierte Prädiktoren werden wahrscheinlich in zukünftigen Prozessoren gedeihen.

Diese Prädiktoren sind Caches in dem Sinne, dass sie Informationen speichern, deren Berechnung kostspielig ist. Ein Teil der Terminologie, die bei der Erörterung von Prädiktoren verwendet wird, ist dieselbe wie die für Caches (man spricht von a Schlag in einem Verzweigungsprädiktor), aber Prädiktoren werden im Allgemeinen nicht als Teil der Cache-Hierarchie angesehen.

Der K8 hält die Befehls- und Datencaches kohärent in Hardware, was bedeutet, dass ein Speichern in eine Anweisung, die der Speicheranweisung eng folgt, diese folgende Anweisung ändert. Andere Prozessoren, wie die der Alpha- und MIPS-Familie, haben sich auf Software verlassen, um den Befehlscache kohärent zu halten. Es ist nicht garantiert, dass Speicher im Befehlsstrom erscheinen, bis ein Programm eine Betriebssystemeinrichtung aufruft, um die Kohärenz sicherzustellen. Die Idee ist, Hardwarekomplexität einzusparen, unter der Annahme, dass selbstmodifizierender Code selten ist.

Implementierung

Zwischenspeicher liest sind die häufigsten CPU-Operationen, die länger als einen einzelnen Zyklus dauern. Die Programmausführungszeit ist in der Regel sehr empfindlich gegenüber der Latenz eines Level-1-Daten-Cache-Treffers. Es wird viel Designaufwand und oft Energie und Siliziumfläche aufgewendet, um die Caches so schnell wie möglich zu machen.

Der einfachste Cache ist ein virtuell indizierter Direct-Mapping-Cache. Die virtuelle Adresse wird mit einem Addierer berechnet, der relevante Teil der Adresse extrahiert und verwendet, um einen SRAM zu indizieren, der die geladenen Daten zurückgibt. Die Daten werden in einem Byte-Verschieber Byte-ausgerichtet und von dort zur nächsten Operation umgangen. Es ist keine Tag-Prüfung in der inneren Schleife erforderlich – tatsächlich müssen die Tags nicht einmal gelesen werden. Später in der Pipeline, aber bevor der Ladebefehl zurückgezogen wird, muss das Tag für die geladenen Daten gelesen und mit der virtuellen Adresse verglichen werden, um sicherzustellen, dass es einen Cache-Treffer gab. Bei einem Fehlschlag wird der Cache mit der angeforderten Cache-Zeile aktualisiert und die Pipeline wird neu gestartet.

Ein assoziativer Cache ist komplizierter, da eine Art Tag gelesen werden muss, um zu bestimmen, welcher Eintrag des Cache ausgewählt werden soll. Ein satzassoziativer N-Wege-Level-1-Cache liest normalerweise alle N möglichen Tags und N Daten parallel und wählt dann die Daten aus, die dem übereinstimmenden Tag zugeordnet sind. Level-2-Caches sparen manchmal Energie, indem sie zuerst die Tags lesen, sodass nur ein Datenelement aus dem Daten-SRAM gelesen wird.

  Lesepfad für einen 2-Wege-assoziativen Cache Lesepfad für einen 2-Wege-assoziativen Cache

Das Diagramm rechts soll verdeutlichen, wie die verschiedenen Felder der Adresse verwendet werden. Die Adressbits sind in Little-Endian-Notation gekennzeichnet: Bit 31 ist höchstwertig; Bit 0 ist am wenigsten signifikant. Das Diagramm zeigt die SRAMs, die Indizierung und das Multiplexing für einen 4 KiB großen, satzassoziativen, virtuell indizierten und virtuell markierten 2-Wege-Cache mit 64 B-Zeilen, einer Lesebreite von 32 b und einer virtuellen Adresse von 32 b.

Da der Cache 4 KiB groß ist und 64 B-Zeilen hat, gibt es nur 64 Zeilen im Cache, und wir lesen zwei gleichzeitig aus einem Tag-SRAM, der 32 Zeilen mit jeweils einem Paar von 21-Bit-Tags hat. Obwohl jede Funktion der virtuellen Adressbits 31 bis 6 verwendet werden könnte, um die Tag- und Daten-SRAMs zu indizieren, ist es am einfachsten, die niedrigstwertigen Bits zu verwenden.

Da der Cache 4 KB groß ist und einen 4-B-Lesepfad hat und bei jedem Zugriff auf zwei Arten liest, ist der Daten-SRAM in ähnlicher Weise 512 Zeilen mal 8 Byte breit.

Ein modernerer Cache könnte 16 KiB groß sein, 4-Wege-Set-assoziativ, virtuell indiziert, virtuell angedeutet und physisch markiert sein, mit 32 B-Zeilen, 32 B-Lesebreite und 36 B-physikalischen Adressen. Die Lesepfadwiederholung für einen solchen Cache sieht dem obigen Pfad sehr ähnlich. Anstelle von Tags werden Vhints gelesen und mit einer Teilmenge der virtuellen Adresse abgeglichen. Später in der Pipeline wird die virtuelle Adresse durch den TLB in eine physische Adresse übersetzt und das physische Tag wird gelesen (nur eines, da der vhint angibt, welcher Weg des Caches gelesen werden soll). Schließlich wird die physische Adresse mit dem physischen Etikett verglichen, um festzustellen, ob ein Treffer aufgetreten ist.

Einige SPARC-Designs haben die Geschwindigkeit ihrer L1-Caches um einige Gate-Verzögerungen verbessert, indem sie den Addierer für virtuelle Adressen in die SRAM-Decoder einbrachen. Siehe Summenadressierter Decoder.