Ich habe mich als ich an dem Aufsatz über die Cray 3 saß, bekam ich immer mehr Infos über die früheren Rechner von Seymour Cray und machte mich an die Cyber 6600 und die hatte schon in den frühen sechziger Jahre ein Problem: Die Logik war schneller als der Speicher. Zeit das vielleicht mal als Thema im Blog aufzugreifen.
Ich glaube das Problem existierte fast von Anfang des Computers. Die ersten Rechner verwandten die gleichen Bauteile für Speicher und CPU, so die Rechner von Zuse oder die ersten Röhrenrechner wie die ENIAC. Doch bald kam man drauf für den Speicher Elemente zu nehmen, die billiger als die Logik waren. Das lag auf der Hand, denn egal wie komplex eine CPU ist, sie braucht immer weniger Elemente als der Speicher. Eine 8-Bit CPU hatte zwischen 4500 und 6500 Transistoren, für den Speicher von 64 KByte hätte man 524.000 Elemente gebraucht die als Flip-Flop Speicherelement rund 2 Millionen Transistoren erfordert hätten. Bei einer 16 Bit CPU waren es zwischen 29.000 und 134.000 Transistoren, für den Speicher hätte man 32 bis 512 Millionen Transistoren gebraucht.
So wich man aus auf Elemente oder Technologien aus, die billiger waren. Zuerst waren es Eisenringe, die man zwar manuell auffädeln musste, aber in der Herstellung waren sie erheblich billiger als die Transistoren aus Germanium. als integrierte Schaltungen aufkamen erfand man bald das DRAM, das anders als das SRAM für jedes Bit nur einen Transistor (anstatt 4-6 je nach Geschwindigkeit) brauchte. Allerdings sind DRAM Speicher relativ langsam, weil das „Bit“ in einem Kondensator steckt und auch das Ansprechen einer Zeile einige Zeit braucht. Die Diskrepanz wurde im Laufe der Zeit immer größer. Die CPU der Cyber 6600 war zehnmal schneller als der Speicher, heute ist es der Faktor 30.
Wie sorgt man dafür, dass die CPU nicht 90% der Zeit warten muss?
Das erste was man einführte war der Prefetch. Anstatt jeden Befehl einzeln zu holen, dann auszuführen, holte die CPU schon die die Bytes die auf den nächsten Adressen folgten. Da zum größten Teil die Befehle linear abgearbeitet wurde konnte man so die Ausführung beschleunigen, da man die Ausführungszeit der Befehle für das Holen der nächsten Bytes nutzen konnte. Prefetch wurde sehr früh eingeführt, beschleunigte aber nur die Ausführung der Befehle, nicht die der Daten die verarbeitet wurden. Das wurde für zahlreiche Rechner zum Bottleneck. Prominente Beispiele waren die STAR 100, der erste Vektorrechner bei dem man den Fehler machte, die Rechenoperationen vom Speicher zum Speicher auszulegen. Auch die Cray 1 war in dieser Hinsicht schlecht ausgelegt. Nur wenn die Daten in Registern standen, war sie schnell. Beim Zugriff auf den Speicher sank die Leistung auf ein Siebtel ab und wenn es keine Vektoroperationen möglich waren, sondern Skalarberechnungen, dann sank die Leistung von maximal 130 auf 4-5 MFlops ab.
Der nächste Schritt war es, den Speicher in Bänken anzuordnen und auf diese nacheinander zuzugreifen. Die beschleunigende Wirkung erklärt sich durch die Arbeitsweise von Speicherbausteinen. Wenn eine CPU Daten haben wollte, legte sie zuerst die Adresse an die Speicherchips an und signalisierte auf einer Leitung, dass sie auf Daten wartete. Der Speicherbaustein übersetzte die Adresse in interne Zeilen- und Spaltenadressen, öffnete den Zugang der Speicherzelle am Kreuzungspunkt der Spalten-/Zeilenadressen zu den Datenleitungen, die Ladung setzte diese unter Spannung und der Baustein setzte eine Leitung unter Spannung die signalisierte, dass die Daten bereit zum Abholen waren. Nun konnte die CPU die Datenleitung abfragen und sie setzte zum Schluss eine Signalleitung zurück, die dem Baustein signalisierte, das die Aktion abgeschlossen ist. Spricht man nun mehrere Bausteine nacheinander an, weil man damit rechnet, die nächsten Bytes gleich zu benötigen (siehe Prefetch), so kann die CPU nacheinander von allen die Daten holen, weil sie im entscheidenden Schritt, dem Daten abholen, warten.
Die Technik nennt man Bankinterleaving.. Es gibt sie auch beim PC, dort spricht man von Speicherkanälen. Wenn man sich das Motherboard anschaut, so hat es mindestens zwei Slots, manchmal auch 4,6 oder 8. Typischerweise bilden zwei Steckplätze eine Bank, das bedeutet wenn man vier Module in den Rechner packt, so müsste er schneller sein als mit zwei. Aufgrund anderer Techniken ist der Vorsprung heute aber im einstelligen Prozentbereich. Das einsetzen von Banks ist sehr verbreitet, genauso wie bei Prefetch gibt es aber Probleme, wenn die Ausführung nicht linear ist, das bedeutet der Code verzweigt (Sprünge, Unterprogramme aufgerufen werden) oder die Daten wechseln (neue lokale Variablen in Unterprogrammen), dann nützt das vorausschauende Lesen der Daten aus der Speicherbank nichts, denn diese werden nicht mehr gebraucht. Ganz fatal ist das beim Code, weil das dann auch den Prefetch und die Pipeline betrifft, die Befehle vorrausschauend holen und Dekodieren. Es gibt auch hier einige prominente Beispiele wo dieser Effekt zuschlug. Beim Pentium Pro war es so, dass bei 16-Bit Zugriffen der Mechanismus nicht griff und der damals sehr teure Prozessor langsamer als ein niedrig getakteter preiswerter Pentium war und beim Pentium 4 gab es auch einen ziemlichen Performanceeinbruch wenn der Code nicht linear war oder Daten nicht im Cache. Ein prominenter Supercomputer der darunter litt war die Cray 2. Man setzte dort Logikchips mit 4,1 ns Zykluszeit ein und Speicherchips mit 160 ns. So waren die Speicherchips in nicht weniger als 64 Bänken angeordnet. ging es linear zu, so wurde eine Bank erst nach 262 ns wieder angesprochen – lange genug bei selbst der langsamen Zugriffszeit. Doch meist war das nicht gegeben und in der Realität erreichte die Cray nur die Hälfte ihrer nominellen Spitzenleistung von 1942 MFlops.
Der Cache ist die letzte Neuerung. Der Gedanke ist der dass man einen lokalen Speicher hat, der schnell genug für die CPU ist. Er ist in einzelne Bereiche, Cachelines unterteilt. Für jede Cacheline wird gemerkt welche Adressen des Speichers sie enthält und wann der letzte Zugriff erfolgte, wenn die aktuell benötigten Daten nicht im Cache waren, dann wurde die älteste Cacheline mit neuen Daten überschrieben (das dauerte nun so lange wie ein normaler speicherzugriff), danach kommen sie aus dem Cache. Dieser muss nun so schnell wie möglich sein. Er befindet sich auch meistens im Prozessor selbst um die Wege kurz zu lassen. Da derartiger Speicher sehr teuer ist, haben heutige Prozessoren meistens gestufte Caches, einen kleinen Level 1 Cache mit hoher Geschwindigkeit, ein größerer Level 2 Cache der etwas langsamer ist und einige Prozessoren wie der Power 5 von IBM haben auch einen Level 3 Cache, der ist dann auch meist außerhalb des Prozessors in einem eigenen Baustein unteegrbracht beim L1 und L2 Cache ist das wegen der Signallautweiten nicht möglich (bei 3 GHz legen Signale maximal 7 cm zurück bis der nächste Takt beginnt). Caches haben einen Vorteil gegenüber den bisherigen Lösungen, die nur einen Speicherbereich schnell machen, aber beim Zugriff auf einen anderen versagen. Dadurch dass der Cache mehrere Bereiche hat, kann er bei Sprüngen und Verzeigungen von verschiedenen Stellen deren Code vorrätig halten, ebenso wie die benötigten Variablen. vorrätig halten. Da Code üblicherweise nicht laufend neue Daten anfasst und auch immer wieder dieselben Routinen nutzt war der Cache die wichtigste Möglichkeit dem Speicher Beine zu machen. Bei den alten Prozessoren konnte man ihn noch im BIOS abschalten und plötzlich war der Rechner nur noch so schnell wie ein IVM AT und die alten DOS spiele liefen wieder ….
Der SDARM Speicher führte im PC Bereich den Burstmodus ein. Die Geschwindigkeitssteigerung liegt darin, dass man die obige aufwendige Kommunikation zwischen Prozessor und Speicher abkürzte. Nach dem ersten Übertragenen Wort liegt das nächste beim nächsten Takt an den Ausgängen ohne das es quittiert wird. Das geht, weil der Chip intern die nächsten Daten lädt und an die Datenleitungen geht. Allerdings ist der Speicher nun nicht mehr universell einsetzbar. Er und der Prozessor müssen mit dem gleichen Takt betrieben werden, da der Speicher nicht mehr darauf wartet dass der Prozessor die Abnahme der Daten quittiert. DDR Speicher ist SDRAM, der zweimal pro Taktzyklus (bei jedem Flankenwechsel) Daten an die Ausgänge stellt.
Bei den Großrechnern üblich, bei Prozessoren nur in kleinem Maße eingesetzt ist das letzte Konzept: Bandbreite. Wenn der Speicher langsam ist und man aus verschiedenen Gründen keine Caches einsetzen kann (bei Großrechnern z. B. wegen der langen Signallaufzeit zwischen den Modulen) dann kompensiert man durch Bandbreite. Wenn man eine 32 Bit Architektur hat, dann lädt man 256 Bit auf einmal, dann hat man die nächsten 16 Worte vorrätig. Im PC Bereich ist vor allem der Cache mit einer hohen Bandbreite angebunden, der Pentium hatte auch einen 64 Bit Datenbus bei einer 32 Bit Architektur. Verbreiteter ist das Konzept bei Grafikkarten, hier bringen Caches wenig, weil hunderte von Recheneinheiten dauernd neue Daten anfordern und im Prinzip auf den ganzen Speicher mehrmals pro Sekunde zugegriffen wird. Hier wird Geschwindigkeit erreicht durch einen Datenbus von 128 bis 512 Bit Breite der dann bei einem Taktzyklus (und der liegt weit unter dem von CPU’s) gleich mal 16 bis 64 Bytes auf einmal transferiert.
Architekturen können sich auch an den Umstand anpassen, das geschah mit Befehlen wie SIMD (Single Instruction multiple Data), bei denen mehrere Daten mit einem Befehl kombiniert werden. Es entfallen dann die Zugriffe auf den Speicher, da die Daten schon am Code hängen oder CLIW (Very Long Instruction Word, bei dem mehrere Befehle zu einem großen Block kombiniert werden – auch hier kann man diesen Block auf einmal holen anstatt mehrmals auf den Speiche zuzugreifen.