Cache (BE: [kaʃ], AE: [kæʃ]) bezeichnet in der EDV einen schnellen Puffer-Speicher, der in unterschiedlichen Geräten wie z. B. CPUs oder Festplatten zum Einsatz kommt.
Ein Cache enthält Kopien von Inhalten eines anderen (Hintergrund-)Speichers (siehe: Speicherhierarchie) und beschleunigt somit den Zugriff darauf. Um den Nutzen des meist mehrere Größenordnungen kleineren Caches im Vergleich zum Hintergrundspeicher zu maximieren, werden bei der Funktionsweise und Organisation eines Caches die Lokalitätseigenschaften der Zugriffsmuster ausgenutzt. Beobachtet man die Aktivität eines laufenden Programms über ein kurzes Zeitintervall, so stellt man fest, dass wiederholt auf wenige kleine Speicherbereiche (z.B. Code innerhalb Schleifen, Steuervariablen, lokale Variablen und Prozedurparameter) zugegriffen wird. Darum können bereits kleine Cache-Speicher mit einigen KByte sehr wirksam sein.
Wörtlich aus dem Englischen übersetzt bedeutet Cache (in britischem Englisch gesprochen kasch, entlehnt vom französischen cacher – verbergen) „geheimes Lager“. Der Name verdeutlicht den Umstand, dass ein Cache seine Arbeit zumeist im Verborgenen verrichtet. Für den Programmierer ist er weitgehend transparent. Seine Existenz tritt nur bei Performance-relevanten Optimierungen oder bei selten nötigen Operationen wie etwa dem Cache-Flush in Erscheinung.
Inhaltsverzeichnis |
Die Ziele beim Einsatz eines Caches sind eine Verringerung der Zugriffszeit bzw. eine Verringerung der Anzahl der Zugriffe auf den zu cachenden Speicher. Das bedeutet insbesondere, dass sich der Einsatz von Caches nur dort lohnt, wo die Zugriffszeit auch signifikanten Einfluss auf die Gesamtleistung hat. Während das bei den meisten (skalaren) Mikroprozessoren der Fall ist, trifft dies z. B. nicht auf Vektorrechner zu, wo die Zugriffszeit keine sehr wichtige Rolle spielt. Deswegen wird dort üblicherweise auch auf Caches verzichtet, weil diese keinen oder nur wenig Nutzen bringen.
Ein weiterer wichtiger Effekt beim Einsatz von Caches ist die verringerte Bandbreitenanforderung an die nächsthöhere Speicherebene der Speicherhierarchie. Dadurch, dass oftmals der Großteil der Anfragen vom Cache beantwortet werden kann (Cache-Hit, s. u.), sinkt die Anzahl der Zugriffe und damit die Bandbreitenanforderung an den zu cachenden Speicher. Ein moderner Mikroprozessor ohne Cache würde selbst mit unendlich kleiner Zugriffszeit des Hauptspeichers dadurch ausgebremst, dass nicht genügend Speicherbandbreite zur Verfügung steht, weil durch den Wegfall des Caches die Anzahl der Zugriffe auf den Hauptspeicher und damit die Anforderung an die Speicherbandbreite stark zunehmen würde. Ein Cache kann daher also auch genutzt werden, um die Bandbreitenanforderungen an den zu cachenden Speicher zu reduzieren, was sich z. B. in geringeren Kosten für diesen niederschlagen kann.
Bei CPUs kann der Einsatz von Caches somit zum Verringern des Von-Neumann-Flaschenhals der Von-Neumann-Architektur beitragen. Die Ausführungsgeschwindigkeit von Programmen kann dadurch im Mittel enorm gesteigert werden.
Ein Nachteil von Caches ist das schlecht vorhersagbare Echtzeitverhalten, da die Ausführungszeit eines Befehls aufgrund von Cache-Misses nicht immer konstant ist.
Da es technisch nicht oder nur sehr schwer möglich ist, einen Cache zu bauen, der gleichzeitig sowohl groß als auch schnell ist, kann man mehrere Caches verwenden – z. B. einen kleinen schnellen und einen großen langsameren Cache (der aber immer noch Größenordnungen schneller ist als der zu cachende Speicher). Damit kann man die konkurrierenden Ziele von geringer Zugriffszeit und Cachegröße (wichtig für Hit-Rate) gemeinsam realisieren.
Existieren mehrere Caches, so bilden diese eine Cache-Hierarchie, die Teil der Speicherhierarchie ist. Die einzelnen Caches werden als Level-1 bis Level-n durchnummeriert (kurz: L1, L2 usw.). Die niedrigste Nummer bezeichnet hierbei den Cache mit der kleinsten Zugriffszeit – welcher also als erstes durchsucht wird. Enthält der L1 Cache die benötigten Daten nicht, so wird der nächste (meist etwas langsamere, aber größere) Cache (also der L2) durchsucht usw. Das geschieht solange, bis die Daten entweder in einem Cache-Level gefunden oder alle Caches ohne Erfolg durchsucht wurden (Cache Miss, s. u.). In diesem Fall muss auf den verhältnismäßig langsamen Speicher zugegriffen werden.
Moderne CPUs haben meist zwei oder drei Cache-Levels. Mehr als drei ist eher unüblich. Festplatten haben nur einen Cache.
Bei CPUs kann der Cache direkt im Prozessor integriert oder extern auf der Hauptplatine platziert sein. Je nach Ort des Caches arbeitet dieser mit unterschiedlichen Taktfrequenzen: Der L1 ist fast immer direkt im Prozessor integriert und arbeitet daher mit dem vollen Prozessortakt – also u. U. mehrere Gigahertz. Ein externer Cache hingegen wird oftmals nur mit einigen hundert Megahertz getaktet.
Gängige Größen für L1-Caches sind 4 bis 256 KiB und für den L2 64 bis 12288 KiB.
Moderne Prozessoren haben getrennte L1-Caches für Programme und Daten (Lese- und Schreibcache), teilweise ist das auch noch beim L2 der Fall (Montecito). Man spricht hier von einer Harvard-Cachearchitektur. Das hat den Vorteil, dass man für die unterschiedlichen Zugriffsmuster für das Laden von Programmcode und Daten unterschiedliche Cachedesigns verbauen kann. Außerdem kann man bei getrennten Caches diese räumlich besser zu den jeweiligen Einheiten auf dem Prozessor-Die platzieren und damit die kritischen Pfade beim Prozessorlayout verkürzen. Des Weiteren können Instruktionen und Daten gleichzeitig gelesen/geschrieben werden, wodurch der Von-Neumann-Flaschenhals weiter verringert werden kann. Ein Nachteil ist, dass selbstmodifizierender Code nicht sehr gut auf modernen Prozessoren läuft. Allerdings wird diese Technik heute ohnehin nur noch sehr selten verwendet.
Bei Festplatten befindet sich der Cache auf der Steuerplatine und ist 1–32 MB groß. Für mehr Informationen siehe auch Festplattencache.
Caches sollen schnell sein. Um dies zu erreichen, verwendet man für den Cache meist eine andere (schnellere) Speichertechnologie als für den zu cachenden Speicher (SRAM ggü. DRAM, DRAM ggü. Magnetscheibe etc.). Daher sind Caches meist wesentlich teurer in Bezug auf das Preis/Bit Verhältnis, weswegen Caches deutlich kleiner ausgelegt werden. Das führt dazu, dass ein Cache nicht alle Daten gleichzeitig vorrätig haben kann. Um das Problem zu lösen, welche Daten denn nun im Cache gehalten werden sollen, werden Lokalitätseigenschaften der Zugriffe ausgenutzt:
Die räumliche Lokalität ist der Grund, warum man bei Caches nicht einzelne Bytes, sondern die Daten ganzer Adressbereiche („Cache-Block“ oder manchmal auch „Cache-Line“ genannt) speichert. Zusätzlich dazu erleichtert es die Implementation, weil man nicht für jedes Byte Daten dessen Adresse im Speicher festhalten muss, sondern nur für jeden Cache-Block (der aus vielen Bytes besteht). Die Wahl der Blockgröße ist ein wichtiger Designparameter für einen Cache, der die Leistung – positiv wie auch negativ – stark beeinflussen kann.
Es existieren drei Möglichkeiten der Cacheorganisation: Direkt abgebildet (direct mapped, abgekürzt mit DM), Vollassoziativ (fully associative, VA) und Satzassoziativ (SA, manchmal auch „Mengenassoziativ“ (set associative) genannt). Die ersten beiden sind ein Spezialfall des satzassoziativen Cache.
Eine kleine Übersicht der drei Typen: Hierzu sei m die Anzahl der Cache-Blocks und n die Assoziativität des Caches.
Typ | Anzahl Sätze | Assoziativität |
---|---|---|
DM | m | 1 |
VA | 1 | m bzw. n |
SA | m/n | n |
Ein DM-Cache ist somit ein SA-Cache mit einer Assoziativität von Eins und so vielen Sätzen wie Cache-Blocks vorhanden sind. Ein VA-Cache ist ein SA-Cache mit einer so großen Assoziativität wie Cache-Blocks vorhanden sind und nur einem Satz.
Den Vorgang, dass die Daten einer Anfrage an einen Cache in selbigem vorrätig sind, bezeichnet man als „Cache-Hit“ (dt.: Cache-Treffer), den umgekehrten Fall als „Cache-Miss“ (dt.: „Cache-Verfehlen“).
Um quantitative Maßzahlen für die Bewertung der Effizienz eines Caches zu erhalten, definiert man zwei Größen:
Dabei werden drei Arten von Cache-Misses unterschieden:
Diese drei Typen bezeichnet man auch kurz als „Die drei C“. In Multiprozessorsystemen kann beim Einsatz eines Cache-Kohärenz-Protokolls vom Typ Write-Invalidate noch ein viertes „C“ hinzukommen, nämlich ein Kohärenz-Miss (engl.: „Coherency Miss“). Wenn durch das Schreiben eines Prozessors in einen Cache-Block der gleiche Block im Cache eines zweiten Prozessors hinausgeworfen werden muss, so führt der Zugriff des zweiten Prozessors auf eine Adresse, die durch diesen entfernten Cache-Block abgedeckt war, zu einem Miss, den man als Kohärenz-Miss bezeichnet.
Bei der Verwaltung des Caches ist es sinnvoll, immer nur die Blöcke im Cache zu halten, auf die auch häufig zugegriffen wird. Zu diesem Zweck gibt es verschiedene Ersetzungsstrategien. Eine häufig verwendete Variante ist dabei die LRU-Strategie (engl. least recently used), welche immer den am längsten nicht mehr zugegriffenen Block im Cache austauscht. Moderne Prozessoren (AMD Athlon uvm.) implementieren meist eine Pseudo-LRU-Ersetzungsstrategie, die also fast wie echtes LRU arbeitet, aber leichter in Hardware zu implementieren ist.
Bei einem Schreibzugriff auf einen Block, der im Cache vorhanden ist, gibt es prinzipiell zwei Möglichkeiten:
Analog zu Obigem gibt es bei einem Schreibzugriff auf einen Block, der nicht im Cache vorhanden ist, prinzipiell ebenso zwei Möglichkeiten:
Einige Befehlssätze enthalten Befehle, die es ermöglichen am Cache vorbeizuschreiben.
Normalerweise wird entweder die Kombination write-back mit write-allocate oder write-through mit non-write-allocate verwendet.
Ein Cache-Flush („Pufferspeicher-Spülen“) bewirkt das komplette Zurückschreiben des Cache-Inhaltes in den Hauptspeicher. Dabei bleibt der Cache-Inhalt meist unangetastet. Ein solches Vorgehen ist nötig, wenn man die Cache-Hauptspeicher-Konsistenz wiederherstellen möchte.
Notwendig ist das immer dann, wenn die Daten von externer Hardware benötigt werden. Beispiele: Multiprozessor-Kommunikation; Übergabe eines als Ausgabepuffer benutzten Teils des Hauptspeichers an den DMA-Controller; Hardware-Register (so genannter Ein-/Ausgabebereich oder I/O-Bereich) – wobei letztere normalerweise gar nicht als zwischenspeicherbar eingestuft werden, d. h. bei ihrem Zugriff wird der Cache umgangen.
Für jeden Cache-Block wird im Cache folgendes gespeichert:
Ein Cache ist „heiß“, wenn er optimal arbeitet (sprich: gefüllt ist und nur wenige Cache-Misses hat), und „kalt“, wenn er dies nicht tut. Ein Cache ist nach Inbetriebnahme zunächst kalt, da er noch keine Daten enthält und häufig zeitraubend Daten nachladen muss, und wärmt sich dann zunehmend auf, da die zwischengelagerten Daten immer mehr den angeforderten entsprechen und wenig Nachladen mehr erforderlich ist. Im Idealzustand werden Datenzugriffe fast ausschließlich aus dem Cache bedient, und das Nachladen kann vernachlässigt werden.
Das Wort „Cache“ trifft man auch in der Software an. Hier beschreibt es dasselbe Prinzip wie bei der Hardwareimplementierung: Daten werden für einen schnelleren Zugriff auf ein schnelleres Medium zwischengespeichert.
Beispiele:
Software-Caches werden meist im Form von temporären Dateien angelegt.
Man spricht auch von Caching, wenn ein Betriebssystem gewisse Ressourcen – wie z. B. Libraries oder Font-Daten – vorerst im Arbeitsspeicher belässt, obwohl sie nach Ende ihrer Benutzung nicht mehr gebraucht werden. So lange kein Speichermangel herrscht, können sie im Arbeitsspeicher verbleiben, um dann ohne Nachladen von der Festplatte sofort zur Verfügung zu stehen, wenn sie wieder gebraucht werden. Wenn allerdings die Speicherverwaltung des Betriebssystems einen Speichermangel feststellt, werden diese Ressourcen als erste überschrieben.