Home Computer Prozessor Basics Site Map counter

Speicherarchitekturen

Seit es Computer gibt, gibt es ein Problem: in der Regel ist der Arbeitsspeicher nicht schnell genug. in diesem Artikel soll es darum gehen, welche Maßnahmen man macht um diese Langsamkeit zu kaschieren. Ignoriert man dies, so kann dies das Aus für eine Technologe bedeuten. So waren sowohl der erste 16 Bit Mikroprozessor TMS 9900 wie auch einer der ersten Vektorrechner, die CDC 100 Star Fehlschläge weil sie aufs falsche Pferd setzten.

Beim TMS 9900, der auch im Heimcomputer Ti 99/4a steckte, hatte Texas Instruments die Vorstellung, das DRAM weiter an Geschwindigkeit zulegen würde. Der Prozessor hatte daher nur drei Allzweckregister. Eines war ein Zeiger auf einen Bereich im Arbeitsspeicher der als 256 weitere Register angesprochen werden konnte. Das Konzept war auf dem Papier elegant, hatten andere Prozessoren zu dieser Zeit doch nur 3-8 Register. Das Problem war aber, das man den Takt von Prozessoren viel schneller steigern konnte, als das DRAM schneller wurde. Damit musste man Wartetakte einschieben und der Prozessor war langsam. Als Texas Instruments dies mit einem Nachfolgemodell, dem TMS 9995 korrigierte (dieser hatte einen kleinen Speicherbereich für die Register auf dem Chip) hatten sich schon andere Architekturen durchgesetzt.

Die Star 100 war wie die Cray 1 ein Vektorrechner. Aber anders als die Cray 1 berechnete sie alle Daten vom Arbeitsspeicher und legte auch die Ergebnisse dort ab. (Die Cray benutzte dazu interne CPU Register). Der verwendete Ringkernspeicher hatte eine Zykluszeit von 1280 ns, das war schon damals (1974) langsam. Obwohl der Takt vergleichsweise niedrig war (25 MHz, die Cray 1 erreichte zwei Jahre später 80 MHz, das Vorgängermodell CDC 7600 auch schon 36,5 MHz) war der Speicher viel zu langsam. Man erhöhte die Bandbreite auf 512 Bit (bei einer 64 Bit Architektur) und unterteilte den Speicher in 32 Bänke. Doch das reichte nicht. Die theoretische Performance von 100 MFLOPS bei einfacher Genauigkeit wurde nur bei Vektoroperationen mit sehr vielen Zahlen erhalten. Schon das Vorgängermodell war schneller, wenn weniger als 50 Zahlen verarbeitet wurden, bei einfachen Rechnungen nützte die Bankunterteilung und der breite Bus gar nichts und die Performance brach radikal ein auf einen Bruchteil des theoretischen Wertes.

CDC machte ein Neudesign ersetzte den langsamen Ringkernspeicher durch 80 ns schnellen Halbleiterspeicher und vermarktete den Rechner als CDC 205. Inzwischen hatte jedoch die Cray 1 nicht nur die Performancekrone an sich gerissen sondern war auch ein Kauferfolg - anders als die CDC 100 Star oder CDC 205.

Das Problem

Zuerst einmal zu einer ganz einfachen Frage: warum ist eigentlich der Speicher zu langsam? Nun dafür kann es verschiedene Gründe geben:

Technologische Gründe

Es kann physikalisch bedingt sein, dass ein Speicher anders aufgebaut ist als die CPU. In der Regel ist die Lösung dann langsamer als die CPU. Beispiele dafür gibt es zu Hauf, es ist sogar eher die Regel aus die Ausnahme:

Der erste eingesetzte Arbeitsspeicher waren Verzögerungsspeicher, bei denen die Werte in Ultraschallwellen in flüssigem Quecksilber gespeichert wurden. Am Ende angekommen konnten die Signale seriell ausgelesen werden oder die Welle erneut aufgebaut werden, wenn man den Wert nicht brauchte. Bedingt durch die relativ langsame Signallaufzeit im Quecksilber und das serielle Auslesen war dies ein langsamer Speicher, aber eine kompakte Röhre konnte dutzende von Bits speichern und brauchte anders als die Vakuumröhren, die man damals für die Logik verwendete, kaum Strom.

Abgelöst wurde der Verzögerungsspeicher durch den Ringkernspeicher. Ringkernspeicher sind kleine Eisenringe, aufgezogen auf eine Drahtmatrix. Zum Schreiben wird eine Zeile und eine Spalte unter Strom gesetzt. Nur am Kreuzungspunkt erreicht das dabei erzeugte Magnetfeld aus, um den dort befindlichen Eisenkern zu magnetisieren bzw. seine Magnetfeldausrichtung zu ändern. Zum Lesen vermisst man die Änderung des Stroms bei Anlegen eines kleineren Stroms durch das Magnetfeld, oftmals gibt es dazu einen dritten Lesedraht der nicht unter Strom steht.

Die Änderung des Magnetfeldes geht nicht sehr schnell, Ringkernspeicher waren daher schon immer langsamer als die Transistoren die Vakuumröhren als Logikelemente ablösten. Aber Eisenringe sind billig, viel billiger als die aufwendig aus Halbleitern hergestellten Transistoren und man konnte sie auch sehr dicht packen, da sie keine Wärme abgaben. Zudem behielten sie sie Information ohne Stromversorgung.

Heute setzt man DRAM als Speicher ein, Dynamischen Random Access Memory. Er ist eine Halbleiterschaltung, doch sie ist anders aufgebaut als die Logik der Prozessoren. Bei DRAM nutzt als Speicherstelle einen Kondensator - in der Praxis eine Zone im Silizium, die sehr stark dotiert ist und in die Tiefe des Chips geht. Daneben oder darüber befindet sich ein Transistor. Er blockiert oder öffnet den Zugang zum Kondensator. Dazu gibt es eine Leitung zu den Datenleitungen und zwei Leitungen zu den beiden Adressleitungen (Spalte und Zeile). Wie bei den Ringkernspeichern bilden die einzelnen Zellen eine Matrix, die durch Zeilen und Spalten adressiert wird. Je ein Signal der Zeile und Spalte führt zum Emitter und der Basis des Transistors, nur wenn beide Leitungen Strom führen wird er leitend und die Ladung aus dem Kondensator erzeugt ein Signal auf der Datenleitung. Dieses wird verstärkt und nach außen auf die Datenleitung zum Chip abgegeben. Je nach Anzahl der Datenleitungen bestehen die Chips intern aus mehreren Feldern. Üblich sind 1, 4 oder 16 Felder, entsprechend 1,4 oder 16 Bits auf einmal.

Kondensatoren geben nur langsam ihre Ladung ab, daher sind DRAM langsamer als Logikbausteine. Ähnliche technische Gründe kann man auch bei Flashspeichern anführen. die anders als DRAM die Daten dauerhaft speichern aber noch langsamer sind. Da Flash Speicher nicht als Arbeitsspeicher eingesetzt wird erspare ich mir eine Erklärung der Funktionsweise genauso wie bei ROM's (Read Onyl Memorys)..

Kostengründe

Meistens nicht zu trennen von den physikalischen Gründen sind die Kostengründe. Denn natürlich gäbe es immer die Möglichkeit die Logikbausteine so zu verschalten, dass man sie zum Speichern von Information nutzen kann. Jeder Prozessor hat auch Speicherplätze, die nach diesem Prinzip arbeiten - die Register. (heute auch Caches, doch dazu weiter unten mehr). Das geschieht normalerweise in einem Flip-Flop. Ein Flip-Flop besteht aus zwei Widerständen und vier Transistoren. Die letzteren sind so verschaltet das Einschalten eines Transistors den nächsten umkippen lässt, so kreist die Information, wenn ein Bit gespeichert wird durch die vier Transistoren. Die Widerstände isolieren das Flip-Flop von den Datenleitungen wenn kein Zugriff erfolgt. Während man aber bei Ringkernspeichern oder dem DRAM ein Element pro Bit braucht, sind es hier sechs. Mehr noch: Die Verschaltung und die Leitungen belegen Platz. Ein Flip-Flop speichert daher auf der gleichen Fläche 16-mal weniger Bits als ein DRAM und durch den komplexeren Aufbau ist der Speicher noch dazu teurer.

Doch selbst wenn man sich entschließt den Speicher in derselben Technologie herzustellen wie die Logik, kann es sein dass man aus Kostengründen Kompromisse machen muss. Die Cray 1 hatte z.B. einen Hauptspeicher aus Flip-flops. Allerdings verwandte man dort Bipolarchips, während man für die Register der CPU die schnelleren ECL-Schaltungen nahm. Auf eine ECL-Schaltung passten 64 Bits, auf eine Bipolar Schaltung dagegen 1024. Dafür hatten die ECL-Schaltungen eine Zugriffszeit von 50 ns, die Bipolarschaltungen eine von 6 ns (und waren immer noch doppelt so schnell wie heute DDR4 Speicher...). Die stromhungrige ECL-Technologie lies keine so hohe Integration zu.

Die Kostengründe ergeben sich schlicht und einfach auch daraus das Logik viel weniger Schaltelemente braucht als der Speicher. Hierzu ein Beispiel. Mein derzeitiger Rechner hat einen iCore Prozessor der vierten Generation "Haswell", an der Typenbezeichnung erkennbar an 4xxx bei den letzten 4 Ziffern (meiner z.B. ein icore i5-4790). Da heißt ein Prozessor etwa 1,4 Milliarden Transistoren - nicht wenig, aber der Arbeitsspeicher besteht bei 8 Gigabyte aus 65,5 Milliarden Transistoren - mehr als 50-mal mehr und trotzdem billiger als der Prozessor. Arbeitsspeicher mit derselben Technologie wie die Prozessoren wäre extrem teuer.

Zugriffszeit und Zykluszeit

Bei fast jedem Speicher kann man mehrere Performanceparameter angeben. Zwei haben fast alle Technologien, das ist die Zugriffszeit und die Zykluszeit. Die erste ist von Bedeutung bei der Wahl des für einen Prozessors geeigneten Speichers. Die meisten Prozessoren können Befehle in einem Takt einlesen. Ein Speicher muss also inem Prozessortakt die Daten auf den Bus legen, nachdem der Prozessor zuerst die Adresse und dann ein Signal "Möchte Daten haben" (Read/Write Select) an den Speicherbaustein übermittelte. Die maximale Taktfrequenz die möglich ist, ist dann der Kehrwert der Zykluszeit. So braucht man wenn der Takt 4 MHz beträgt Speicherbausteine mit einer Zykluszeit von 1 s / 4 MHz = 250 ns.

Physikalisch definiert ist die Zugriffszeit als die Zeit die vergeht zwischen der Anforderung an den Speicher, dass man Daten haben will bis diese auf dem Bus liegen und eingelesen werden können. In dieser Zeit muss ein DRAM Baustein z.B. die Adresse in Spalten/Zeilenadresse seiner Felder umsetzen, diese Leitungen unter Strom setzen, der Transistor der Speicherstelle muss schalten, die Ladungen aus dem Kondensator auf die Datenleitung strömen und von einem Leseverstärker verstärkt und an die Pins am Ausgang geleitet werden.

Der zweite wichtige Wert ist die Zykluszeit, denn alle Technologien haben nun eine Phase in der der Strom von den Leitungen genommen werden muss, der Transistor braucht Zeit, die Ladung abzubauen die sich aufbaute als er leitfähig wurde und zahlreiche Technologien können auch die Information nicht zerstörungsfrei auslesen (z.B. ist dies bei DRAM so), das bedeutet sie muss neu geschrieben werden. Die Zykluszeit gibt an, wann man erneut auf einen Speicherbaustein zugreifen kann. Das ist wichtig wenn ein Programm nach dem ersten Zugriff erneut Daten braucht z.B. der Befehl schnell abgearbeitet ist und man den nächsten holt oder der Befehl Daten braucht, die man auch aus dem Speicher braucht. Die Zykluszeit sollte kleiner sein als die Ausführzeit der schnellsten Befehle oder der Zeit nach der Operanden die zu einem Befehl gehören, geholt werden, was bei einer Pipeline oft schon im nächsten Takt der Fall ist.

Dpeziell für DRAM gibt es noch eine Reihe anderer Performanceparameter die man heute auch im BIOS einstellen kann und als Ziffernfolge bei den Speichermodulen findet (dann in Vielfachen des Basistaktes). Doch da es hier um Grundlagen geht, belasse ich es dabei.

Wie kann man die Langsamkeit kaschieren?

Die meisten Methoden, die vermeiden, dass ein Prozessor nicht durch den langsamen Speicher ausgebremst wird, beruhen auf einer einfachen Tatsache: der hohen Lokalität von Daten und Code.

Das bedeutet Befehle folgen in der Regel aufeinander. Das gilt eigentlich für alle Befehle mit zwei Ausnahmen. Das eine sind Sprünge. Sprünge braucht man bei Schleifen, um wieder an den Anfang der Schleife zu springen oder einer If Then Else Anweisung um den Else Teil oder Then Teil der Anweisung zu überspringen wenn dieser nicht zutrifft. Diese Sprünge sind jedoch noch meist lokal, sprich Schleifen sind normalerweise klein, ebenso die Anweisungen bei Then oder Else. Damit findet man die Daten vielleicht in den gerade gelesenen oder in den Befehlen die unmittelbar folgen.

Anders ist es bei Unterprogrammaufrufen, die können an weiter entfernte Stellen oder sogar ganz andere Regionen im Speicher (Betriebssystem) erfolgen. Diese durchbrechen in jedem Fall das Prinzip der Lokalität.

Bei den Daten ist es so, dass Variablen die zu einer Routine gehören, sowohl von Menschen wie auch Compilern hintereinander angeordnet werden. Größere Datenstrukturen belegen dann sowieso größere Speicherbereiche und ihre Elemente folgen aufeinander (z.B. bei Zeichenketten oder indizierten Feldern),

Was Daten von Code unterscheidet ist, dass es es bei Code viel häufiger vorkommt, das ein Block sehr oft ausgeführt wird, während die Daten eine höhere Fluktuation aufweisen. (Eine Schleife die alle Elemente eines Arrays bearbeitet wird oft durchlaufen, jedes Element des Arrays aber nur einmal).

Alle Methoden setzen diese Tatsache, dass Elemente die nacheinander bearbeitet werden (bei Code und Daten) aufeinanderfolgend im Speicher abgelegt werden und bei Code es wahrscheinlich ist, das Schleifen sehr häufig ausgeführt werden.

Bank interleaving

Die älteste Methode ist das Bank Interleaving. Sie ist erstmals dokumentiert in der IBM Stretch, die 1961 erschien dokumentiert. Diese setzte 4 Speicherbänke ein. Bank interleaving  teilt den Speicher in einzelne Bereiche ("Banks") ein. Auf diese wird nacheinander zugegriffen. Das geht am einfachsten, indem man die letzten Bits der Adresse nutzt um die Bank anzusprechen. Nutzt man z.B. die letzten drei Bits, so greift man beim Erhöhen der Adresse um 1 auf die nächste Bank zu. Die erste kommt erst nach 2^3 = 8 Zugriffen erneut dran. In diesem Fall könnte die Zugriffszeit des Speichers achtmal so groß sein wie bei einer Bank. (Meist orientiert man sich aber an der längeren Zugriffszeit bei der Auslegung der Bankzahl). Es liegt aber auf de Hand dass die Beschleunigung nur greift, wenn dieses Muster auch vorliegt. Das Worst Case Szenario ist gegeben, wenn ein Zugriff laufend auf Adressen erfolgt die ein vielfaches der Bankzahl sind, also in diesem Fall, vielfache von 8. Da Daten und Code an unterschiedlichen Stellen liegen ist es sehr wahrscheinlich dass selbst ein Prozessor auf mindestens zwei Bänke zugreift.

Bei Mehrprozessorsystemen muss zudem gewährleistet sein, dass diese nicht kurz hintereinander auf dieselbe Bank zugriefen. Je mehr Prozessoren es gibt, desto schwerer wird dies. Die Einbußen bei Mehrprozessorsytemen sind daher oft größer als bei einem Einprozessorsystem. Dazu kommen noch Zugriffe von Ein-/Ausgabeelementen, die einen direkten Speicherzugriff haben (DMA) wie Festplatten, Netzwerkadapter etc. Bei der CDC 8600 die vier Prozessoren hatte und einen langsamen Ringerkernspeicher erhalten sollte untersuchte man dies und kam auf 10-15 gleichzeitig von 32 Bänken.

Um zu vermeiden das man auf eine Bank zugreift bevor sie bereit ist, gibt es mehrere Möglichkeiten. Die erste ist die Daten immer auf auf vielfache der Bankzahl anzuordnen, auch wenn man Bytes zwischen den Variablen leer lässt. So ist bei einem 64 Bit Windows der Standardwert für viele Compiler, Variablen auf vielfache von Quad-Words (8 Byte) anzuordnen. Das zweite, seltener eingesetzte ist, die Zahl der Bänke so zu wählen, dass sie eine Primzahl ist. Im obigen Beispiel also nicht 8 sondern 7 oder 11 Bänke zu nehmen. Der Grund: Es gibt auch Verzögerungen, wenn auch nicht so starke wenn man in ganzzahligen Bruchteilen der Bankzahl zugreift, wie 2 oder 4 in diesem Beispiel. Vor allem wird die Auslegung als Primzahl wichtig bei Mehrprozessorsystemen. Da man schaltungstechnisch die untersten Bits sehr einfach als Steuerleitungen nutzen kann sind aber Bankzahlen in Potenzen von 2 gängiger.

Heute kann ein PC zwei Speicherkanäle (= zwei Bänke) nutzen. Server noch mehr, bis zu 16 Bänke. Intern sind DDR3 RAM in acht Bänken organisiert, DDR4 sogar in 16 Bänken.

Bank Interleaving hat seine Grenzen. So setzte Seymour Cray in der Cray 2 DRAM ein, einen Speicher der dreimal langsamer als das vorher eingesetzte RAM war, während der Taktzyklus um den Faktor 6 anstieg und die Prozessorenzahl um den Faktor 4. Um dies zu kompensieren gab es nicht weniger als 128 Bänke. (Vorgängermodell: 8 oder 16). Bei einem Prozessor und linearem Zugriff gab es so keine Verzögerung, doch es gab nicht nur lineare Zugriffe und es gab vier Prozessoren und noch einen Vordergrundprozessor der die ein/Ausgabedaten schrieb. Als Folge war ein Zugriff auf eine Bank die noch nicht bereit war Daten zu liefern so häufig, das die Cray 2 in der Praxis nur die Hälfte ihrer theoretischen Spitzenleistung erreichte.

Cray setzte beim Nachfolgemodell Cray 3 dann auf das schnellere SRAM. Obwohl die Cray 3 nur 64 Bänke hatte war nun das RAM schnell genug (Zugriffszeit 4 anstatt 25 Takte) und bremste den Rechner nicht mehr aus.

Bandbreite

Eine zweite Möglichkeit ist es Geschwindigkeit durch Bandbreite zu kompensieren. Ein Prozessor der z.B. eine 32 Bit Architektur hat, könnte über den Datenbus 64, 128 oder 256 Bit auf einmal laden und in einem schnellen Pufferspeicher zwischenspeichern. Wenn man dann die nächsten Bytes braucht, findet man diese im Pufferspeicher und muss nicht den Speicher bemühen. Auch hier hat man mehrere Speicherbausteine die parallel arbeiten. Im Unterschied zum Bank Interleaving liefern diese aber gleichzeitig ihre Daten, anstatt nacheinander und haben dann genügend Zeit ihre Zykluszeit zu durchlaufen bis der nächste Zugriff erfolgt, da nun ja erst die Daten aus dem Puffer ausgelesen werden. Sind diese nicht mehr gültig, weil es eine Verzweigung gibt oder der Datenblock nicht gebraucht wird, so hat man sie umsonst übertragen, aber in jedem anderen Falle profitiert man davon. In Systemen mit einem Cache bietet es sich an die Datenbusbreite auf die Größe eine Cacheline auszulegen, da dies sowieso die kleinste Einheit ist, die der Cache (stellvertretend für den Prozessor) anfordern will.

Mehr Bandbreite scheitert oft an der nötigen Hardware. So müssen entsprechend viele Datenleitungen vorhanden sein. Das bedeutet bei Großrechnern zahlreiche Drähte und Pins auf den Platinen bei PC's zahlreiche Leitungen auf den Platinen und Pins im Prozessorsockel. Es gibt hier einfach technische Grenzen was wirtschaftlich machbar ist, zumal sehr viele Leitungen auch wie Antennen und Sender wirken und Störungen auf benachbarten Leitungen bewirken. Trotzdem war diese Technik bei Großrechnern weit verbreitet zumal dort auch der Platz für Leitungen und die Anschlüsse vorhanden war (eine CPU erstreckte sich über mehrere Platinen). Bei Großrechnern waren Datenbusse von 256 und 512 Bit Breite keine Seltenheit.

Oftmals findet man dieses Konstrukt bei Prozessoren die kurze Befehle haben, aber sehr viele Daten mit einem Befehl bearbeiten (VLIW oder SIMD Architekturen). Das sind z.B. Signalverarbeitungsprozessoren. Im kleinen findet man es aber auch im PC Bereich so hatte der Pentium einen 64-Bit Datenbus, obwohl er eine 32 Bit Architektur hatte.

der größte Einsatz heute ist in Grafikkarten: Grafikkarten müssen laufend aus dem Speicher Texturen holen und über das Bild legen. Sie brauchen zwar verhältnismäßig Speicher nutzen diesen aber in der ganzen Größe dauernd, während PC Code große Lokalität hat. Caches machen bei ihnen daher keinen Sinn. Um trotzdem viele Daten schnell an die vielen Shadereinheiten zu bekommen haben sie breite Datenbusse von 128 bis 1024 Bit Breite.

Prefetch

Selten als eigene Speicherarchitektur, aber oft vermischt mit anderen Architekturen ist der Prefetch. Er kann vom Prozessor ausgehen oder vom Speichersystem. Beim Prozessor ist er dadurch, dass die meisten Maschinenbefehle aufeinander folgen in den meisten Chips implementiert. In der x86 Serie wurde der Prefetch schon bei dem 80286 eingeführt. Prefetch bedeutet, dass man die Daten vorrausschauend liest. Da Instruktionen aufeinander folgen, kann das die Abarbeitung beschleunigen. Sie landen dann in einem Puffer. Ohne andere Maßnahmen wie Interleaving bringt Prefetch eine Geschwindigkeitssteigerung, wenn der Speicher nicht beschäftigt ist. Braucht ein Befehl längere Zeit zur Ausführung oder beschäftigt er nur die Rechenwerke der CPU, so wird der Speicher nicht genutzt. Eine CPU kann diese Totzeiten nutzen, um vorrausschauend die Daten zu laden und in einem Puffer zu speichern. Beim 80286 kam erstmals vor, dass der Prozessor bei höherem Takt schon schneller als der damals eingesetzte Speicher war.  Der Prefetch konnte das ausgleichen, indem er zumindest nach komplexeren Befehlen durch das vorrausschauende Lesen die Daten schon hatte. Ansonsten musste der Prozessor einen Wartetakt einschieben.

Im Speichersystem gibt es auch Prefetch. Hier holt das Speichersystem von sich aus die Daten von den Adressen die nach der folgen die gerade abgeruft wird und speichert sie in einem schnellen Puffer zwischen. Werden dann diese Adressen abgefragt so stehen die Daten schon in dem Puffer und können mit geringerer Verzögerung ausgeliefert werden. Zahlreiche Großrechner setzen das ein. Anders als der Instruktionsprefetch der Prozessoren beschleunigt dies auch den Datenzugriff.

Eine moderne Form des Prefetches ist der Burst-Zugriff, der beim DDR-RAM zur Verfügung kommt. Fordert man von DDR-Speicher ein Datenwort an, so fällt nur beim ersten Zugriff die Zugriffszeit an. Intern ruft die Logik die Daten von den folgenden Zellen ab. Sie stehen dann nach Abruf der ersten an den Ausgangsleitungen. Da sich die Zugriffszeit von Speicherbausteinen in den letzten zwei Jahrzehnten kaum verbessert hat (sie liegt bei DDR3 Bausteinen bei 10 bis 15 ns, also ausreichend für eine Taktfrequenz von 66 bis 100 MHz) ist dies die einzige Möglichkeit mehr Daten zu transferieren. Die Anzahl der Bytes die transferiert werden ist bei DDR-Speicher auf die Länge von Cachelines der Prozessoren (aktuell: 8 Transfers mit zusammen 64 Byte) abgestimmt. Sie werden ohne Anforderung des Prozessors bei den nächsten Takten übertragen (ab DDR2 zweimal pro Takt). Da die Speicherbausteine nicht schneller wurden hat man dabei das Prefetching immer weiter erhöht: DDR hatte zweifaches Prefetching (abrufen von zwei Bänken parallel), DDR2 vierfaches und DD3 achtfaches Prefetching. DDr4 wird sechszehn Bänke einsetzen.

Buffer

Buffer und Caches sind zwei Techniken die die Lokalität dahingehend nutzen, dass ein Teil des Codes und der Daten sich in einem schnellen, wenn auch teuren Speicher befinden. Da dieser Speicher nur die Daten aufnehmen muss, die man aktuell braucht kann er klein ausfallen ohne das die Performance leidet.

Die ältere form ist der Instruktion Buffer. In einem Instruktion Buffer werden einfach die letzten Anweisungen aufbewahrt. Da es im Code viele Rückwärtssprünge gibt, beschleunigt das die Abarbeitung. Zahlreiche Rechner haben Instruktionsbuffer so alle Rechner von Seymour Cray. Bessere Implementierungen kombinieren dies mit dem Prefetch und enthalten im Buffer auch die Instruktionen die noch erst abgearbeitet werden. Schlussendlich gibt es auch Sprünge nach vorne. Üblich ist aber nicht dass die Aufteilung des Buffers symmetrisch ist, sondern eher dass nur einige Anweisungen im Voraus gelesen werden, eben wegen des Risikos das sie gar nicht gebraucht werden und weil Rückwärtssprünge häufiger sind.

Auch ohne eigene dezidierte Instruction Puffer kann eine CPU einen Speicher für die letzten Anweisungen haben, nämlich wenn sie eine Instruktionspipeline hat. Dann liest der Prozessor bei jedem Takt eine Instruktion und fängt mit der Abarbeitung an. Solange wie diese nicht abgeschlossen ist durchläuft die Instruktion eine Pipeline, die pro Befehl vorhanden sein muss. Die Zahl der Pipleines richtet sich dann wieder nach der Anzahl der Takte pro Befehl. Typisch sind 4-10 Pipelinestufen. Extremwerte sind 2 und 20. Ein theoretisches Optimum wurde bei 8-9 Stufen ermittelt.

Rechner können auch Datenpuffer haben, die dann aber meist programmgesteuert gefüllt werden. Ein Compiler weis ja schon zur Erstellzeit welche Daten verarbeitet werden müssen und kann diese vorrausschauend lesen. Die Cray 2 und 3 hatten solche Datenpuffer, um die Abhängigkeit von dem langsamen Hauptspeicher zu kompensieren.

Caches

Voll assoziativer CacheAuch ein Cache ist ein lokaler Speicher, aber ein erheblich flexibleres Konzept. Ein Cache ist ursprünglich vor den Prozessor geschaltet. Er bekommt vom Prozessor die Adresse übermittelt. Danach erfragt der Prozessor vom Cache die Daten genauso wie er diese vom Speicher erfragen würde. Der Cache muss nun damit er nicht langsamer als der Hauptspeicher ist folgendes tun:

Aus dieser Beschreibung wird klar, das ein Cache nur einen Nutzen hat, wenn er zum einen schneller als der Hauptspeicher ist und zum anderen möglichst viele benötigte Daten im Cache sind. Weiterhin gibt es als Nebenbedingung, dass das Suchen im Cache schnell gehen muss, sonst geht der Geschwindigkeitsgewinn verloren. Ein Prozessor muss in der Regel auf den Cache angepasst sein. Damit der Cachecontroller Zeit hat die Daten zu suchen und damit der normale Zugriff (aus dem Hauptspeicher) nicht langsamer durch die Vorschaltung des Caches abläuft, hat intel beim 80386 die Adressierung so geändert, das das Signal für "Will Daten haben" zusammen mit der Adresse einen Takt früher auf dem Bus lag, bevor der Prozessor überhaupt bereit war, die Daten zu holen. Dieser Takt war die Zeit, die der Cache für seine Suche hatte.

Caches bestehen aus schnellen Speicherbausteinen. Der erste wurde bei dem IBM Modell 360 in den höheren Modellen eingeführt, das war Mitte der Sechziger Jahre. Caches sind unterteilt in kleine Einheiten fester Länge den Cachelines. Die Länge hängt von den Prozessorarchitektur aber auch der Geschwindigkeit des Speichers und des Caches ab. Der Trend geht zu längeren Cachelines. Beim 80386 waren 4-8 Byte üblich, heute sind es meist 64 Byte. Zu diesem Speicherbereich, der nun Daten des Arbeitsspeichers abpuffert (als Kopie hält) gibt es noch eine Indextabelle, sie enthält die Adresse die in jeder Cacheline gecacht wird. Caches bestehen aus den schnellsten Speicherbausteinen die möglich sind. Heute aus statischem RAM (SRAM) den oben erwähnten Flip-Flops aus schnell schaltenden Transistoren).

Die Organisation der Indextabelle und der Cachelines geschieht nun über drei Verfahren:

direkt mapped CacheVoll assoziativer Cache: Jede Cachline puffert einen Bereich des Arbeitsspeichers ab, die Indextabelle enthält die Adressen die zuletzt aktiv waren. Neue Anfragen die nicht im Cache sind, führen zum Verwerfen der ältesten Cacheline. Dieser Cache kann, wenn man aus einem Speicherbereich viele Daten braucht, diese cachen und andere Bereiche des Speichers auf die gerade nicht zugegriffen werden werden nicht gechacht. Es ist der leistungsfähigste denkbare Cache. Der Peis ist, das man um festzustellen, ob sich Daten im Cache befinden man die gesamte Indextabelle durchsuchen muss. So eine Indextabelle war schon bei einem 80386 Prozessor mit einem kleinen 8 KByte Cache 2000 Einträge lang. Diese in einem Takt zu durchsuchen wird selbst bei schnellen Logikschaltungen nicht möglich sein. Voll assoziative Caches sind selten im Einsatz gewesen. (Bild oben)

Das Gegenteil ist der direkt gemappte Cache. Hier entspricht jede Cacheline einem Speicherblock. Nehmen wir an es gäbe 2048 Cachelines bei einem 16 Megabyte großen Hauptspeicher. Dann würde für jeden 8 Kbyte (16 MByte/2048) großen Speicherblock genau eine Cacheline genutzt werden. In der Indextabelle steht dann die Adresse (wobei die untersten 13 Bits ausreichen) die gecacht wird. Da es eine direkte Zuordnung gibt ist das nachschauen ganz einfach: die obersten 11 Bits geben den Index in den Indextabelle an, in der Tabelle müssen nun nur noch die untersten 13 Bits mit der Anforderung verglichen werden.

Der Nachteil des direkt mapped Caches ist das der starken Lokalität nicht Rechnung trägt. Das kann man nur durch eine sehr lange Cacheline, die dann größere Datenmengen oder viele Instruktionen cacht ausgleichen. Viele Cachelines verweisen auf Speicherbereiche auf die selten zugriffen wird oder niemals (man wird ja nie den ganzen Speicher belegen, sondern hat immer auch ungenutzte Speicherbereiche). Dafür ist die Umsetzung dieses Verfahrens in einen Cachecontroller sehr einfach und sehr schnell. (Bild rechts)

Der beste Kompromiss ist der n-fach assoziative Cache. Er besteht aus mehreren Direkt-gemappten Caches . Die Anzahl dieser gibt das n an. damit gibt es pro Block nicht eine Cacheline sondern n Stück. Umgekehrt muss beim Suchen nach den richtigen Eintrag nicht hunderte von Einträgen untersucht werden, sondern genau n Einträge. Innerhalb eines Blocks ist dann wieder die 1:1 Umsetzung von Adresse und Cachline möglich. Auch n ist im Laufe der Zeit größer geworden. Beim 80386 lag n noch bei 2, bei einem Icore der vierten Generation bei 16. (Bild unten links).

Der Geschwindigkeitsgewinn hängt stark von der Geschwindigkeit des Hauptspeichers, Caches, Größe und Organisation ab. Intel untersuchte dies beim 80386. Setzt man die Performance des Arbeitsspeicher mit 1,00 so erreicht man mit statischem Speicher (keine Wartezeit) 1,47 fache Geschwindigkeit. Ein 64 KByte großer Cache erreicht je nach Organisation 1,39 bis 1,42 (am langsamsten direkt gemappter Cache). Wichtig ist auch die Cache Hit Rate, das heißt, wie viele Zugriffe erfolgten auf dem Cache. Das waren bei nur 64 KByte Größe schon 77-93%. Zu kleine Caches mit zu geringen Hit Rates verlangsamen übrigens den Prozessor. Im obigen Beispiel hätte ein nur 1 KByte großer Cache die Ausführung verlangsamt.

N-fach assoziativer CacheHeute sind Caches mehrstufig. Das bedeutet es gibt einen kleinen Cache auf den der Prozessor direkt zugreift (L1 oder First Level Cache), findet dieser die Daten nicht so sucht er in einem größeren, aber langsameren Cache nach, dem L2-Cache und dieser eventuell dann in einem noch größeren L3 Cache. Bei Mehrkernprozessoren ist es meist so, das jeder Prozessor einen eigenen L1 Cache hat und die anderen (zumindest der L3 Cache) gemeinsam genutzt werden. Die Größe orientiert sich auch nach Einsatzzweck. Serverprozessoren oder Rechner die wissenschaftliche Daten verarbeiten haben oft einen sehr großen Datendurchsatz. Sie haben meist größere Caches als Desktopprozessoren bei denen Programme mehr Lokalität aufweisen.

Auch ist es sinnvoll zwischen Daten und Code zu unterscheiden. Daten sind in der Regel volatiler vor allem aber ändern sie sich, während Code sich nicht verändert (selbst modifizierender Code ist wegen Viren heute meist bei Prozessoren verboten und löst eine Exception aus de das Betriebssystem benachrichtigt).

Caches sind nicht trivial, weil immer gewährleistet sein muss, das Hauptspeicher und Cache den gleichen Inhalt haben. Das betrifft sowohl den Fall, das der Prozessor Daten verändert (der Cache muss die Daten in der Cacheline austauschen und an den Hauptspeicher weitergeben). Er kann dies gleichzeitig tun (Write through) oder warten bis der Hauptspeicher gerade Zeit dafür hat (write back). Auf den Hauptspeicher können aber auch andere Geräte zugreifen wie der DMA Zugriff von Festplatten. Dann stimmen die Daten im Cache nicht mehr. Zudem wird es bei CISC Architekturen aufwendiger die Daten zu übertragen weil CISC Architekturen byteorientiert sind, also man gerade mal nicht den Eintrag in der Cacheline braucht sondern das dritte Byte vom Anfang an. In der Regel wird daher ein Cache mit einem Datenbus einheitlicher Breite und CPUs mit Prefetch kombiniert, sodass immer eine ganze Cacheline ausgeliefert werden kann.

Aufgrund des gesagten ist klar, dass Caches durchaus eine Rechnerarchitektur verkomplizieren können. Daher gibt es auch viele Beispiele wo sie nicht verwendet wurden. Seymour Cray hat in keinem seiner Supercomputer Caches verwendet. Ebenso hatten die ersten RISC.Architekturen als Mikroprozessoren SPARC, MIPS, ARM keine Caches. Doch da Speicher nicht in dem Maße schneller wurde wie Prozessoren schneller wurden gibt es heute keinen Prozessor mehr ohne Cache ausgenommen einfache Mikrokontroller bei denen aber dann auch das RAM und ROM im Prozessor stecken.

Register

Last but nicht least die grundlegendste Maßnahme: Mehr Register in der CPU. Je mehr Register eine CPU hat, desto mehr Daten kann sie in diesen Halten und für weitere Berechnungen nutzen oder um Zwischenergebnisse zu speichern. Leider ist die Registerzahl eine grundlegende Architekturentscheidung und sie findet sich im Maschinencode wieder. Das bedeutet, dass eine Änderung später kaum möglich ist (die x86 Linie hat seit dem Urahn 8086 z.B. nur vier Allzweckregister AX,BX,CX und DX). Viele Architekturen die auch Caches verzichteten hatten sehr viele Register, so die Supercomputer von Cray oder die MIPS RISC Architektur,

Der Nutzen

Der Nutzen dieser Techniken ist bei modernen Rechnern unübersehbar. DRAM ist seit den ersten Chips, die Anfang der Siebziger Jahre erschienen, etwa um den Faktor 10 schneller geworden. Im gleichen Zeitraum stieg der Takt von Mikroprozessoren um den Faktor 1000. Es ist also offensichtlich, das ohne Techniken die die Langsamkeit kaschieren, Rechner niemals die heutige Geschwindigkeit erreichen können.

Jeder PC Prozessor aber auch jeder Prozessor für Smartphones oder Tablett PCs hat heute Caches. In der Regel mehrstufige Architekturen. Die Größe erreicht schon bei billigen CPU für Low-Cost Geräte 1 Megabyte und kann bei Serverprozessoren auf bis zu 48 MByte anwachsen. Die prozessorinternen Caches sind gestuft in schnelle L1-Caches meist getrennt für Daten und Code, langsamere L2 Caches, ohne Trennung und meist von mehreren CPU's gemeinsam genutzte L3 Caches. Schon aufgrund der hohen Taktgeschwindigkeiten sind sie heute alle in den Prozessor gewandert, den bei 3 GHz und einem Cache der innerhalb von wenigen Takten die Daten liefern soll´, kann man sich de langen Signalwege zu einem Cache außerhalb des Prozessorgehäuses nicht leisten (zumal durch jedes Pins und jede Leitung durch den Wiederstand noch das Signal verzöget wird).

Aus dem gleichen Grund sind heute die Speicherbausteine direkt neben der CPU angebracht. DDR-Speicher, hat in 12 Jahren den Bustakt von 133 auf 266 MHz erhöht, dabei hören bezahlbare Module (nicht für Übertater) aber bei 200 MHz auf. Der Takt täuscht aber über die Verzögerungen hinweg. Das in meinem Rechner verbaute RAM hat eine Zugriffszeit von 13,5 ns, das entspricht einer Taktfrequenz von 74 MHz, wenn es keine anderen beschleunigen Maßnahmen gäbe. Mit dieser Verzögerung von 13,5 ns erfolgt auch der erste Zugriff.

Das besondere an DDR-RAM ist, das der Speicher intern mit mehreren Bänken arbeitet und dort wiederum mehrere Speicherzellen auf einmal abfragt, also intern mit Prefetch arbeitet. Die Zahl der Bänke und die Größe des Prefetches ist dabei von Generation zu Generation größer geworden. Dadurch stehen nach dem ersten Zugriff 64 Byte bereit zum Abholen. Die überträgt der Speicher dann automatisch und zwei seit DDR2 bei jedem Flankenwechsel (bei DDR1 war es noch bei jedem Taktsignal ein Transfer. Bildet man die Multiplikation von Bustakt x Datenbreite des Speichers, so erhält man die Transferrate in Gigabit pro Sekunde. Bei meinem PC ist PC10600 Speicher eingebaut, der Speichertakt beträgt 166 MHz, DDR-RAM überträgt immer 64 Bit auf einmal das sind also 166,66 x 64 = 10666 MBit oder 10,66 Gigabit pro Modul.

DDR-RAM nutzt auch die Erhöhung der Datenrate durch mehr Bandbreite, denn er besteht aus einzelnen Chips die nur vier, acht oder 16 Datenleitungen haben (man braucht dann 16, 8 oder 4 Chips um auf die 64 Datenleitungen zu kommen). Moderne CPU nutzen auch das Banking und haben mehr als einen Speicherkanal (mindestens 2, meistens 4, dies sind die Steckplätze, die sie auf dem Mainboard sehen. Wenn dort mehr Module sind so verteilen sie die Zugriffe auf die Module, sodass Verzögerungen sich nicht so stark auswirken.

Bei Benchmarktests bringt die volle Bestückung aller Speicherslots meist aber nur eine Geschwindigkeitssteigerung von wenigen Prozent, das liegt daran, dass schon der Cache die meisten Zugriffe abfängt. Heute ist es nicht mehr möglich den Cache abzuschalten. Als das noch möglich war konnte man damit den Rechner enorm stark ausbremsen. Man kann den Effekt aber durch ein Programm simulieren, indem man eine große Datenstruktur anlegt und zufällig (oder wenn man weiß wie der Compiler sie anlegt, mit Absicht) auf sie so zugreifen, das Caches nicht wirken. In der Ct tat dies einmal Andreas Stiller um Compilerswitches und die Unterstützung für die AVX Prozessorerweiterung zu testen. Bei einem an sich trivialen Benchmark, einer Matrixmultiplikation sank die Performance von 3,2 GFLOPs pro Prozessor auf 0,133 GFlops ab, wenn man den Zugriff so legte, das die Caches unwirksam waren. Das ist ein Faktor 25 in der Performance!

Referenzen:

Andreas Stiller, ct 12/2014 S.174: Matrix reloaded Von Matrizen und magischen Compiler-Fähigkeiten.

Intel: 80386 Hardware Reference Manual

Harvey G. Cragon: Memory Systems and Pipelined Processors

Hockney, Jesshope: Paralell Computers

Artikel erstellt am 20.09.2014

Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.

Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.

Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.

Mehr über das Buch auf dieser eigenen Seite.

Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.


© des Textes: Bernd Leitenberger. Jede Veröffentlichung dieses Textes im Ganzen oder in Auszügen darf nur mit Zustimmung des Urhebers erfolgen.
Sitemap Kontakt Impressum / Datenschutz Neues Hier werben / advertisment here Buchshop Bücher vom Autor Top 99