Warum hat ein Prozessor 32 Register?

52

Ich habe mich immer gefragt, warum Prozessoren bei 32 Registern stehen blieben. Es ist mit Abstand das schnellste Teil der Maschine. Warum nicht einfach größere Prozessoren mit mehr Registern herstellen? Würde das nicht bedeuten, weniger ins RAM zu gehen?

Matt Capone
quelle
2
Ich denke, über einen bestimmten Punkt hinaus passen alle Ihre lokalen Variablen in die Register. Die tatsächlichen Daten, mit denen Sie arbeiten, sind wahrscheinlich sowieso zu groß
Niklas B.
14
Abnehmende Renditen. Offensichtlich sind Register (in verschiedener Hinsicht) "teurer" als RAM, oder wir hätten nur 8 GB Register.
David Richerby
5
Einer der Gründe, warum es so schnell ist, ist, dass es nicht viele von ihnen gibt.
StackErr
5
Es gibt einen Unterschied zwischen der Anzahl der Register, die die CPU insgesamt hat, und der Anzahl, die Sie gleichzeitig verwenden können.
Thorbjørn Ravn Andersen
CPUs und GPUs verbergen die Latenz hauptsächlich durch Caches bzw. Multithreading. CPUs haben also nur wenige Register, während GPUs Zehntausende von Registern haben. Siehe mein Umfragepapier in der GPU-Registerdatei, in der alle diese Kompromisse und Faktoren erörtert werden.
User984260

Antworten:

82

Erstens wurden nicht alle Prozessorarchitekturen bei 32 Registern gestoppt. Fast alle RISC-Architekturen, bei denen 32 Register im Befehlssatz verfügbar sind, haben tatsächlich 32 Ganzzahlregister und 32 weitere Gleitkommaregister (also 64). (Gleitkommazahl "add" verwendet andere Register als Ganzzahl "add".) Die SPARC-Architektur verfügt über Registerfenster. Auf dem SPARC können Sie nur auf 32 Integer-Register gleichzeitig zugreifen, aber die Register verhalten sich wie ein Stapel und Sie können jeweils 16 neue Register pushen und einfügen. Die Itanium-Architektur von HP / Intel enthielt 128 Ganzzahl- und 128 Gleitkommaregister im Befehlssatz. Moderne GPUs von NVidia, AMD, Intel, ARM und Imagination Technologies stellen eine enorme Anzahl von Registern in ihren Registerdateien zur Verfügung. (Ich weiß, dass dies auf die NVidia- und Intel-Architektur zutrifft. Ich bin nicht sehr vertraut mit den Befehlssätzen AMD, ARM und Imagination, aber ich denke, dass die Registerdateien dort auch groß sind.)

Zweitens implementieren die meisten modernen Mikroprozessoren eine Registerumbenennung , um unnötige Serialisierung zu vermeiden, die durch die Notwendigkeit der Wiederverwendung von Ressourcen verursacht wird. Daher können die zugrunde liegenden physischen Registerdateien größer sein (96, 128 oder 192 Register auf einigen Computern) Es ist erforderlich, dass der Compiler so viele eindeutige Registernamen generiert und dem Scheduler dennoch eine größere Registerdatei zur Verfügung stellt.

Es gibt zwei Gründe, warum es schwierig sein kann, die Anzahl der im Befehlssatz enthaltenen Register weiter zu erhöhen. Zunächst müssen Sie in der Lage sein, die Registerkennungen in jeder Anweisung anzugeben. Bei 32 Registern ist ein 5-Bit-Registerspezifizierer erforderlich, sodass Befehle mit 3 Adressen (wie sie bei RISC-Architekturen üblich sind) 15 der 32 Befehlsbits ausgeben, um nur die Register zu spezifizieren. Wenn Sie diesen Wert auf 6 oder 7 Bit erhöhen, haben Sie weniger Platz, um Opcodes und Konstanten anzugeben. GPUs und Itanium haben viel größere Anweisungen. Größere Anweisungen sind mit Kosten verbunden: Sie müssen mehr Anweisungsspeicher verwenden, damit das Verhalten des Anweisungscaches weniger ideal ist.

Der zweite Grund ist die Zugriffszeit. Je größer Sie einen Speicher machen, desto langsamer ist es, auf Daten von ihm zuzugreifen. (Nur in Bezug auf die Grundphysik: Die Daten werden im zweidimensionalen Raum gespeichert. Wenn Sie also Bits speichern , beträgt der durchschnittliche Abstand zu einem bestimmten Bit .) Eine Registerdatei ist nur eine kleiner Speicher mit mehreren Ports und eine der Einschränkungen beim Vergrößern besteht darin, dass Sie möglicherweise langsamer mit dem Takten Ihres Computers beginnen müssen, um die größere Registerdatei aufzunehmen. Normalerweise ist dies in Bezug auf die Gesamtleistung ein Verlust. O ( nO(n)

Wandering Logic
quelle
1
Ich hätte die 256 FPRs und 32 zusätzlichen Nicht-Fenster-GPRs von SPARC64 VIIIfx erwähnt, die durch Hinzufügen eines Set XAR-Befehls erzielt wurden, der jeweils 13 Bits für die nächsten ein oder zwei Befehle bereitstellt. Es war auf HPC ausgerichtet, sodass die Registeranzahl verständlicher ist. Ich wäre auch versucht gewesen, einige der Kompromisse und Techniken zu erläutern, die mit mehr Registern verbunden sind. Aber Sie haben die Weisheit gezeigt, eine erschöpfendere (und auch dann nicht erschöpfende) Antwort zu vermeiden.
Paul A. Clayton
2
Es könnte sich lohnen, ein wenig auf den nachlassenden Nutzen von mehr Registern für "Allzweck" -Code hinzuweisen, obwohl es nicht einfach ist, aussagekräftige Messungen zu finden. Ich denke, Mitch Alsup erwähnte auf comp.arch, dass die Erweiterung von x86 auf 32 statt 16 Register 3% an Leistung gewonnen hätte, verglichen mit (ISTR) 10-15% für die ausgewählte Erweiterung von 8 auf 16 Register. Selbst für einen Load-Store-ISA bietet der Wechsel zu 64 wahrscheinlich nur geringe Vorteile (zumindest für den aktuellen GP-Code). (Übrigens, GPUs teilen sich häufig Register über Threads hinweg: zB ein Thread mit 250, wobei insgesamt 16 für andere Threads privat bleiben.)
Paul A. Clayton
Es ist interessant zu sehen, dass das Umgebungsmanagement (daher Alpha-Konvertierung), das häufig mit Hochsprachen in Verbindung gebracht wird, tatsächlich auf Registerebene verwendet wird.
Babou
@ PaulA.Clayton Ich dachte immer, dass IA-64 die Architektur ist, die die größte Anzahl von ISA-Registern hat
phuclv
@ LưuVĩnhPhúc Der SPARC64 VIIIfx war HPC-spezifisch. Zu Ihrer Information , der Am29k (eingeführt um 1987-8 ) hatte 64 globale und 128 Fenster-GPRs, was mehr GPRs ist als Itanium (das 8 Zweigregister und ein Schleifenzählregister hat, dessen Funktion in GPRs in einigen anderen ISAs wäre).
Paul A. Clayton
16

Nur zwei weitere Gründe, um die Anzahl der Register zu begrenzen:

  • Wenig zu erwartender Gewinn: CPU wie aktuelle Intel / AMD x64-Modelle verfügen über 32 KByte und mehr L1-D-Cache, und der Zugriff auf den L1-Cache dauert normalerweise nur einen Taktzyklus (im Vergleich zu ungefähr hundert Taktzyklen für einen vollständigen einzelnen RAM) Zugriff). Es gibt also wenig zu gewinnen, wenn mehr Daten in Registern gespeichert sind als im L1-Cache
  • Zusätzliche Rechenkosten: Wenn mehr Register vorhanden sind, entsteht ein Overhead, der einen Computer möglicherweise langsamer macht:
    • In Multitasking-Umgebungen muss ein Task-Switch normalerweise den Inhalt aller Register des Prozesses speichern, die im Speicher verbleiben, und die des einzugebenden Prozesses laden. Je mehr Register Sie haben, desto länger dauert dies.
    • Ebenso verwenden in Architekturen ohne Registerfenster kaskadierte Funktionsaufrufe denselben Registersatz. Eine Funktion A, die eine Funktion B aufruft, verwendet also denselben Registersatz wie B selbst. Daher muss B den Inhalt aller von ihm verwendeten Register speichern (die immer noch die Werte von A enthalten) und vor der Rückkehr zurückschreiben (in einigen Aufrufkonventionen ist es die Aufgabe von A, den Inhalt des Registers zu speichern, bevor B aufgerufen wird, aber die Overhead ist ähnlich). Je mehr Register Sie haben, desto länger dauert das Speichern und desto teurer wird ein Funktionsaufruf.
Robert Buchholz
quelle
Wie funktioniert es für den L1-Cache, damit wir nicht dasselbe Problem haben wie für die Register?
Babou
4
Bei Hochleistungsprozessoren beträgt die L1-Dcache-Latenz in der Regel 3 oder 4 Zyklen (einschließlich Adressenerzeugung), z. B. hat Haswell von Intel eine Latenz von 4 Zyklen (ohne Datenabhängigkeitsregister ist die Latenz auch leichter in der Pipeline zu verbergen). Dcache unterstützt auch tendenziell weniger Zugriffe pro Zyklus (z. B. 2 Lesen, 1 Schreiben für Haswell) als eine Registerdatei (z. B. 4 Lesen, 6 Schreiben für Alpha 21264, die die Datei repliziert hat, 2 Dateien mit 4 Lesevorgängen sind schneller als 1 mit 8).
Paul A. Clayton
@ PaulA.Clayton: Wenn der L1-Cache eine Latenzzeit von 3-4 Zyklen aufweist, könnte dies darauf hindeuten, dass es von Vorteil ist, z. B. ein paar Sätze von 64 Wörtern eines Einzelzyklus-Speichers mit einem eigenen Adressraum von 64 Wörtern zu haben dedizierte "load / store direct" -Anweisungen, insbesondere, wenn es eine Möglichkeit gibt, alle Nicht-Null-Werte abzuspeichern, gefolgt von einem Wort, das angibt, welche Wörter nicht Null sind, und dann eine Möglichkeit, sie zurückzuspeichern (Nullen von nicht aufgetauchten Registern) . Viele Methoden haben zwischen 16 und 60 Wörter mit lokalen Variablen, so dass eine Verkürzung der Zugriffszeit für diejenigen von 3-4 Zyklen auf einen hilfreich erscheint.
Supercat
@supercat Verschiedene Stack (und globale / TLS [z. B. Knapsack]) - Cache-Ideen wurden in wissenschaftlichen Arbeiten vorgestellt sowie Mechanismen wie der Signaturpuffer ( PDF ). Tatsächliche Verwendung, nicht so sehr (wie es scheint). Dies wird immer gesprächiger (sollte also wahrscheinlich enden oder woanders hingehen).
Paul A. Clayton
4

Viele Codes verfügen über viele Speicherzugriffe (30% sind eine typische Zahl). Davon sind typischerweise etwa 2/3 Lesezugriffe und 1/3 Schreibzugriffe. Dies liegt nicht daran, dass die Register nicht mehr ausreichen, sondern dass auf Arrays, Objekt-Member-Variablen usw. zugegriffen wird.

Dies muss im Arbeitsspeicher (oder Datencache) erfolgen, da C / C ++ erstellt wird (alles, was Sie für einen Zeiger erhalten können, muss eine Adresse haben, die möglicherweise im Arbeitsspeicher gespeichert werden muss). Wenn der Compiler erraten kann, dass Sie mit verrückten indirekten Zeigertricks nicht willkürlich in Variablen schreiben, werden sie in Registern abgelegt, und dies funktioniert gut für Funktionsvariablen, aber nicht für global zugängliche (im Allgemeinen alles, was aus malloc herauskommt ()) weil es im Grunde unmöglich ist zu erraten, wie sich der globale Zustand ändern wird.

Aus diesem Grund ist es nicht üblich, dass der Compiler mit mehr als 16 allgemeinen Verwendungsregistern überhaupt etwas anfangen kann. Deshalb gibt es in allen gängigen Architekturen ungefähr so ​​viele (ARM hat 16).

MIPS und andere RISCs haben in der Regel 32, weil es nicht sehr schwer ist, so viele Register zu haben - die Kosten sind niedrig genug, so dass es ein bisschen wie ein "Warum nicht?" Ist. Mehr als 32 sind meistens unbrauchbar und haben den Nachteil, dass der Zugriff auf die Registerdatei länger dauert (jede Verdoppelung der Anzahl der Register fügt möglicherweise eine zusätzliche Multiplexerschicht hinzu, die etwas mehr Verzögerung hinzufügt ...). Außerdem werden die Anweisungen im Durchschnitt etwas länger. Wenn Sie also Programme ausführen, die von der Bandbreite des Befehlsspeichers abhängen, verlangsamen Ihre zusätzlichen Register Sie tatsächlich!

Wenn Ihre CPU in Ordnung ist und keine Registerumbenennung durchführt und Sie versuchen, viele Operationen pro Zyklus auszuführen (mehr als 3), benötigen Sie theoretisch mehr Register, wenn die Anzahl der Operationen pro Zyklus steigt. Deshalb hat der Itanium so viele Register! Aber in der Praxis wird der meiste Code, abgesehen von numerischem Gleitkomma oder SIMD-orientiertem Code (in dem Itanium wirklich gut war), viele Speicher-Lese- / Schreibvorgänge und -Sprünge aufweisen, die diesen Traum von mehr als 3 Operationen pro Zyklus unmöglich machen (insbesondere in serverorientierter Software wie Datenbanken, Compilern, Hochsprachenausführung wie Javascript, Emulation usw.). Dies ist, was Itanium versenkte.

Auf den Unterschied zwischen Berechnung und Ausführung kommt es an!

Hubert Lamontagne
quelle
2

Wer sagt Ihnen, dass der Prozessor immer 32 Register hat? x86 hat 8, ARM 32-Bit und x86_64 hat 16, IA-64 hat 128 und viele weitere Nummern. Sie können einen Blick hier . Selbst bei MIPS, PPC oder Architekturen mit 32 Allzweckregistern im Befehlssatz ist die Anzahl viel größer als 32, da es immer noch Flag-Register (falls vorhanden), Steuerregister ... ohne umbenannte Register und Hardwareregister gibt

Alles hat seinen Preis. Je größer die Anzahl der Register ist, desto mehr Arbeit haben Sie beim Taskwechsel, desto mehr Platz benötigen Sie in der Befehlskodierung. Wenn Sie weniger Register haben, müssen Sie nicht viel speichern und wiederherstellen, wenn Sie Funktionen aufrufen und von Funktionen zurückkehren oder Aufgaben wechseln, ohne dass das Fehlen von Registern in einem rechenintensiven Code in Frage kommt

Außerdem ist es umso teurer und komplexer, je größer die Registerdatei ist. SRAM ist der schnellste und teuerste Arbeitsspeicher und wird daher nur im CPU-Cache verwendet. Aber es ist immer noch viel billiger und nimmt weniger Fläche ein als eine Registerdatei mit der gleichen Kapazität.

phuclv
quelle
2

Beispielsweise hat ein typischer Intel-Prozessor "offiziell" 16 Ganzzahl- und 16 Vektorregister. In Wirklichkeit gibt es aber noch viel mehr: Der Prozessor verwendet die "Registerumbenennung". Wenn Sie eine Anweisung reg3 = reg1 + reg2 haben, hätten Sie ein Problem, wenn eine andere Anweisung, die reg3 verwendet, noch nicht beendet wurde - Sie könnten die neue Anweisung nicht ausführen, falls sie reg3 überschreibt, bevor sie von der vorherigen Anweisung gelesen wurde.

Daher gibt es ungefähr 160 echte Register. Die einfache Anweisung oben wird also in "regX = reg1 + reg2" geändert, und denken Sie daran, dass regX reg3 enthält. Ohne Umbenennungsregister wäre die Ausführung in der falschen Reihenfolge im Wasser absolut tot.

gnasher729
quelle
1

Ich bin kein Elektrotechniker, aber ich denke, eine andere Möglichkeit, die Anzahl der Register zu begrenzen, ist das Routing. Es gibt eine begrenzte Anzahl von Recheneinheiten, und sie müssen in der Lage sein, Eingaben aus jedem Register zu übernehmen und in jedes Register auszugeben. Dies gilt insbesondere dann, wenn Sie über Pipeline-Programme verfügen, die viele Anweisungen pro Zyklus ausführen können.

Eine einfache Version davon hätte eine Komplexität von , was eine Erhöhung der Anzahl von Registern unskalierbar macht oder auf andere Weise eine Neugestaltung des Routings auf etwas viel komplizierteres erfordert, um alles mit einer besseren Komplexität zu routen.O(n2)

Die Idee zu dieser Antwort kam mir, als ich einige von Ivan Godards Vorträgen über die Mill-CPU sah. Ein Teil der Innovation der Mill-CPU besteht darin, dass Sie nicht in beliebige Register ausgeben können - die Ausgänge werden alle auf einen Registerstapel oder ein "Band" verschoben, wodurch Routing-Probleme reduziert werden, da Sie immer wissen, wohin die Ausgabe gehen wird. Beachten Sie, dass sie immer noch das Routing-Problem haben, die Eingangsregister zu den Recheneinheiten zu bringen.

Informationen zur Problemstellung und zur Lösung von Mill finden Sie unter Die Mill-CPU-Architektur - der Gürtel (2 von 9) .

Realz Slaw
quelle
"Sie müssen in der Lage sein, Eingaben aus jedem Register und Ausgaben in jedes Register zu übernehmen." - Ich würde erwarten, dass dies normalerweise mit einem Bus implementiert wird. Es muss nicht für jedes Register eine separate Verbindung zu den ALUs bestehen.
user253751
1
@immibis: Wenn Sie Daten in 300 Pikosekunden verschieben möchten, funktioniert dies mit einem Bus nicht. Und wenn Sie viele Daten verschieben möchten (z. B. um drei Befehle mit zwei Operanden und jeweils einem Ergebnis im selben Zyklus auszuführen), funktioniert ein Bus absolut, absolut nicht.
gnasher729
0

Für die MIPS ISA, Hennessy und Patterson, Computer Organization und Design, 4. Auflage, S. 176, beantwortet diese spezielle Frage direkt:

Kleiner ist schneller. Der Wunsch nach Geschwindigkeit ist der Grund, warum MIPS 32 Register hat und nicht viel mehr.

Olsonist
quelle