Informatik/Rechnerarchitekturen 2.md
2020-12-04 11:37:17 +01:00

19 KiB
Raw Blame History

title date author
Rechnerarchitekturen 2 Wintersemester 20/21 Robert Jeutter

Einführung

Rechnerarchitektur = Programmierschnittstelle + Interner Aufbau\ Einheit von Struktur und Funktion

  • Programmierschnittstelle
    • Schnittstelle zwischen Rechner und Benutzer bzw. der Hardware und der untersten Softwareschicht
    • Befehlssatzarchitektur (Instruction Set Architecture)
  • Interner Aufbau
    • Hardware-Aufbau von Komponenten, die die Rechnerarchitektur realisieren
    • Speichereinheiten, Recheneinheiten, Verbindungssysteme,

Abstraktionsebenen eines Rechnersystems | Anwendungsprogramm | Java, C,... | | Assemblerprogramm | Betriebssystem-Ebene | | Maschinenprogramm | Betriebssystem-Ebene | | Register Transfer Verhaltensebene | Reg[2]:=Reg[3] | | Register Transfer Strukturebene | Addierer, Multiplexer, Register | | Gatterebene | f=a\vee bc | | Transistorebene | |

Grundarchitekturen:

  • Harvard (Zugriff direkt durch Prozessor)
  • Princton/von-Neumann (Zugriff über Systembus)

| Speicher | Daten und Instruktionen speichern | | Steuerwerk | beinhaltet Programmzähler um Ausführung zu steuern | | Rechenwerk | auch ALU (Arithmetic and Logic Unit) um Berechnung durchzuführen Üblicherweise besitzt eine Recheneinheit (CPU) Daten- oder Rechenregister (Registermaschine). Berechnungen werden ausschließlich mit den Registern vorgenommen.

  • Daten aus Hauptspeicher in Register laden
  • Berechnungsaufgaben durchführen
  • Ergebnisse in Hauptspeicher ablegen

Klassifikation von Befehlssatzarchitekturen

  • 0-Operand (Stack): Add
  • 1-Operand (Akkumulator): Add R1
  • 2-Operand: Add R1, R3
  • 3-Operand: Add R1, R2, R3

Prozessorarchitektur

Programmiermodelle, Instruction Set Architectures (ISAs): Klassifikation von Befehlssätzen nach der Gestaltung/Ausprägung der vorhandenen Maschinenbefehle

CISC RISC MIPS
Complex Instruction Set Computing Reduced Instruction Set Computing Microprocessor without interlocked pipeline stages
Einfache und komplexe Befehle Nur einfache Befehle
Heterogener Befehlssatz Orthogonaler Befehlssatz
Verschiedene Taktzahl pro Befehl Meist 1 Takt pro Befehl
Viele Befehlscode-Formate mit unterschiedlicher Länge Wenige Befehlscode-Formate mit einheitlicher Länge
Mikroprogrammwerk Direktverdrahtung
Vermischung von Verarbeitungs- und Speicherbefehlen Trennung von Verarbeitungs- und Speicherbefehlen
schwierig, unter CPI = 2 zu kommen CPI möglichst nicht über 1

Unter dem CPI (cycles per instruction) -Wert einer Menge von Maschinenbefehlen versteht man die mittlere Anszahl der Taktzyklen pro Maschinenbefehl

Einzelzyklusmaschine

  • Programmzähler (32 bit, PZ, engl. Program Counter, PC)
    • Speichert und liefert die aktuelle auszuführende Instruktionsadresse
    • an den Instruktionsspeicher (b) und das Addierwerk (a)
    • übernimmt die Adresse der Folgeinstruktion (c)
  • Addierwerk
    • Fortlaufende Addition mit 4, da 4-Byte Instruktionen
    • Der neue Wert für PZ wird im Register gespeichert (c)
    • Die hintersten 2 Bit im PZ sind immer Null
  • Instruktionsspeicher
    • Liefert die auszuführende Maschineninstruktion
  • Instruktionswort (32 bit)
    • Gelesener Wert erscheint am Ausgang des Speichers
    • Instruktionsformat bestimmt den weiteren Ablauf
  • Master-Slave Flip-Flops
    • Master übernimmt Wert bei steigender Taktflanke
    • Slave übernimmt Wert bei fallender Taktflanke
    • Instruktionszyklus beginnt bei fallender Taktflanke
  • Ansteuerung des Registersatzes
    • Register immer auslesen (kein Takt) und Transport zur ALU
    • Schreiben des Zielregisters Register[rd] am Ende der Taktperiode
    • Zeit für Speicherzugriff und für die primäre ALU muss eingeplant werden
    • Ausgabe des Instruktionsspeichers wird über die ganze Dauer gehalten
  • Vorzeichenerweiterung des Direktoperanden von 16 auf 32 Bit
    • Erleichtert die Unterbringung kleiner Konstanten im Befehlswort
    • Vom Steuerwerk aus abschaltbar für „unsigned“ Befehle

Ausführungsphase

  • ALU-Registeroperationen
    • Operanden im Register oder als
  • Direktoperand
    • Üblicher Satz an ALU-Operationen
    • Register $0 liefert Wert 0
  • Adressierung von Variablen im Speicher
    • Adressrechnung in der primären ALU
    • Basisregister plus Direktoperand
    • Registerinhalt lesen/schreiben
  • Load/Store-Architektur
    • Speicheroperationen können keine Arithmetik
    • also z.B. kein inc 0xaff0($7),0x0004
    • ALU schon zur Adressberechnung benötigt
  • Separater Addierer zur Sprungzielberechnung
    • Prüfschaltung auf Gleichheit zweier Register in der primären ALU („eql“)
    • Bedingte Sprünge mit einem 16-bit Direktoperanden (beq $7,$8,loop)
    • Maximal möglicher Offset von ±17 Bit nach einer 2-bit Verschiebung
    • Unbedingte Sprünge mit 28-bit Adresse später

Speicherzugriff

  • Getrennte Speicher für Code & Daten
    • Aktuelle Instruktion wird bis zum Ende des Gesamtzyklus gehalten
    • Kein zweiter Zugriff im gleichen Taktzyklus möglich
  • Quellregister speichern, falls Store
    • Speichersteuerung durch besonderes Schreibsignal
  • Zielregister laden
    • Falls Ladebefehl aus dem Speicher
    • Falls Rücksprungadresse (PC-magic)
    • Falls Resultat aus ALU
  • ALU-Resultat nutzen
    • Für „Register Write-Back“
    • Als Datenspeicheradresse
    • Nicht direkt speichern, wg. Load/Store-Architektur!

Register zurückschreiben

  • Nummer des Zielregisters (Zielregisterselektor)
    • Stammt aus IR[15-11] oder IR[20-16], 5-bit Bereich für Werte 0-31
  • Steuersignal
    • Zielregister zum Ende des Instruktionszyklus schreiben
    • Schreibsignal an Registersatz, falls nötig
  • Pseudorelative Sprünge (jump xLabel)
    • Kein separater Addierer erforderlich, nur ein zusätzlicher MUX-Eingang
    • Oberste 4 Bits unverändert, untere 28 Bits werden ersetzt (4, 26, 2)
    • Jump-and-Link (jal) sichert alten Programmzähler in $31 (Subroutine)

erforderliche Steuerleitung

  • Für Speicher
    • 2-bit Steuersignal: 0/8/16/32 Bit zum Datenspeicher schreiben
    • Instruktionsspeicher liest immer
  • Für Registersatz
    • 2-bit Steuersignal: 0/8/16/32 Bit zum Registerfile schreiben
  • Für 4 Multiplexer
    • 2-bit Steuersignal: Auswahl des Zielregisters (1 aus 3)
    • 2-bit Steuersignal: Datenquelle für Zielregister
    • 2-bit Steuersignal: Sprungziel wählen
    • 1-bit Steuersignal: Direkt- oder Registeroperand für ALU
  • Für Arithmetik
    • 1-bit Steuersignal: Vorzeichenerweiterung ja/nein
    • 6-bit Steuersignal: ALU-Operation

Einzyklusmaschine ist unwirtschaftlich

  • Komponenten arbeiten jeweils nur einen kleinen Teil der Gesamtzeit
  • Zeitverlust bei potentiell kurzen Instruktionen

Mehrzyklen CPU

  • Gesamtzyklus der bisherigen MIPS
    • Dauer des Instruktionszyklus ist die Summe der Einzelverzögerungen
    • Unteraktivitäten müssen abwarten, bis die Parameter sicher vorliegen
    • Anderenfalls können sich „spurious writes“ ergeben
    • z.B. in Registersatz oder in den Speicher
  • Mehrzyklen-CPU als Überleitung zum Fließbandprinzip
    • Aufteilung der Befehlsausführung auf mehrere gleich lange Taktzyklen
    • Einfügen von Registern für in den Stufen entstandene Zwischenresultate
    • Noch immer nur eine Instruktion zu einem Zeitpunkt in Ausführung
    • CPU-Zustand bezieht sich auf eine einzelne aktuelle Instruktion
  • Pipelined CPU mit Fließbandprinzip
    • In jedem Taktzyklus beginnt eine neue Maschineninstruktion
    • Mehrere Instruktionen gleichzeitig in Ausführung
    • Aber unterschiedlicher Fertigstellungsgrad
    • Bessere Auslastung der Hardware
    • Höherer Durchsatz
  • Große Pipeline-Tiefe:
    • Zusätzliche Ressourcen, höherer Energieaufwand (Taktfrequenz!)
    • Längere Instruktionssequenzen für gleichen oder besseren Speedup (→ Registeroverhead!)
    • Bei unterschiedlichen Stufenverzögerungen bestimmt die langsamste Stufe die Taktfrequenz
  • Lange Instruktionssequenzen:
    • Meist wegen Daten- und Kontrollabhängigkeiten nicht machbar
    • Hohe Latenz Füllen und Leeren der Pipeline!
  • Warum geht die Anzahl der Pipeline-Stufen zurück?
    • hoher Energieverbrauch
    • hohe Leistungseinbußen durch Kontroll- und Datenabhängigkeiten (Füllen/Leeren der Pipeline)
    • mehr Parallelität in den einzelnen Pipeline-Stufen → superskalare Prozessoren
    • mehr Prozessorkerne mit geringerer Leistungsaufnahme pro Kern
  • Fließband-Architektur (engl. pipeline architecture): Bearbeitung mehrerer Befehle gleichzeitig, analog zu Fertigungsfließbändern.

Aufgaben der einzelnen Phasen

  • Befehlsholphase
    • Lesen des aktuellen Befehls; separater Speicher, zur Vermeidung von Konflikten mit Datenzugriffen
  • Dekodier- und Register-Lese-Phase
    • Lesen der Register möglich wegen fester Plätze für Nr. im Befehlswort
  • Ausführungs- und Adressberechnungsphase
    • Berechnung arithmetischer Funktion bzw. Adresse für Speicherzugriff
  • Speicherzugriffsphase
    • Wird nur bei Lade- und Speicherbefehlen benötigt
  • Abspeicherungsphase
    • Speichern in Register, bei Speicherbefehlen nicht benötigt

Pipeline-Hazards

Structural Hazards (deutsch: strukturelle Abhängigkeiten oder Gefährdungen): Verschiedene Fließbandstufen müssen auf dieselbe Hardware-Komponente zugreifen, weil diese nur sehr aufwändig oder überhaupt nicht zu duplizieren ist.

Definition: Ein Befehl i heißt von einem nachfolgenden Befehl j antidatenabhängig, falls j eine Speicherzelle beschreibt, die von i noch gelesen werden müsste.

Definition: Zwei Befehle i und j heißen voneinander Ausgabeabhängig, falls i und j die selbe Speicherzelle beschreiben.

  • Gleichheit der Register wird schon in der instruction decode-Stufe geprüft
  • Sprungziel wird in separatem Adressaddierer ebenfalls bereits in der instruction decode-Stufe berechnet
  • Sofern weiterhin noch Verzögerungen auftreten:
    • nächsten Befehl einfach ausführen (delayed branch)
    • oder weiterhin NOOP(s) einfügen (stall)

Pipelining Zusammenfassung

  • Die Fließbandverarbeitung (engl. pipelining) ermöglicht es, in jedem Takt die Bearbeitung eines Befehls abzuschließen, selbst wenn die Bearbeitung eines Befehls ≥ 1 Takte dauert
  • Mehrere Pipelines -> pro Takt können mehrere Befehle beendet werden
  • 3 Typen von Gefährdungen des Fließbandbetriebs:
    • resource hazards
    • control hazards
    • data hazards (RAW, WAR, WAW)
  • Gegenmaßnahmen
    • pipeline stall
    • branch prediction
    • forwarding / bypassing
    • delayed branches
    • out-of-order execution
    • dynamic sched

Sprungvorhersage

Je mehr die Parallelität ausgenützt werden soll, desto mehr sind Kontrollkonflikte der limitierender Faktor!

Dynamische Sprungvorhersage

  • Zur Laufzeit durch Prozessor-Hardware
  • Vorhersage, ob ein bedingter Sprung genommen wird oder nicht
  • Abhängig von der Vorhersage: Füllen der Prozessor-Pipeline mit Befehlen ab der vorhergesagten Programm-Stelle
  • Reduktion der branch penalty, falls vorhergesagtes Programm-Verhalten mit tatsächlichem übereinstimmt

Einfache lokale Prädiktoren

  • Liefern Vorhersage, ob bedingter Sprung genommen wird oder nicht

  • Prädiktion allein anhand der Historie des betrachteten, aktuellen Sprungs

  • Historie eines Sprungs wird mit 1, 2 oder n Bits gepuffert

  • Sprungvorhersage-Puffer

    • Branch prediction buffer oder branch history table
    • Kleiner Speicher, der mit (Teil der) Adresse des Sprungbefehls indiziert wird
      • Verwendet nur wenige untere Bits der Adresse
      • Enthält 1 Bit: Sprung beim letzten Mal ausgeführt (taken) oder nicht (not taken)
    • Prädiktion: Sprung verhält sich wie beim letzten Mal
    • Nachfolgebefehle ab vorhergesagter Adresse holen
    • Falls Prädiktion fehlerhaft: Prädiktionsbit invertieren
  • Einfachste Art von Puffer (keine Tags, d.h. keine Überprüfung, ob Adresse tatsächlich im Puffer)

    • Entspricht sehr einfachem Cache
    • Hat eine bestimmte Kapazität
    • Kann nicht für alle Sprünge (aktuelle) Einträge enthalten
  • Reduziert branch penalty nur, wenn branch delay länger als Berechnung der Zieladresse mit branch prediction buffer dauert

    • Prädiktion kann fehlerhaft sein
    • Prädiktion kann von anderem Sprungbefehl stammen (mit gleichen Bits im Indexteil der Adressen)
  • Nachteile des einfachen 1-Bit Vorhersageschemas

    • Höhere Fehlerrate als überhaupt möglich, wenn Häufigkeit der Sprungentscheidungen betrachtet wird
    • D.h. auch wenn Sprung fast immer ausgeführt (taken) wird, entstehen 2 Fehler anstatt 1

2-Bit Branch-Prediction Buffer

  • Speicherung der Historie, Befehlsadressen als Zugriffsschlüssel:

Allgemein: n-Bit Prädiktor (Spezialfall: 2-Bit)

  • Verwendet n-Bit Zähler
    • Sättigungsarithmetik (kein wrap around bei Überlauf)
    • Kann Werte zwischen 0 und 2 n - 1 annehmen
    • Wenn Zähler größer als Hälfte des Maximums (2 n - 1): Vorhersagen, dass Sprung ausgeführt wird; ansonsten vorhersagen, dass Sprung nicht genommen wird
    • Zähler wird bei ausgeführtem Sprung inkrementiert und bei nicht ausgeführtem dekrementiert
  • In der Praxis: 2-Bit Prädiktor ähnlich gut wie n-Bit Prädiktor
    • In den meisten Prozessoren heute: 2-Bit Prädiktor für (lokale) Vorhersage
  • Einschränkung des n-Bit (bzw. 2-Bit) Prädiktors:
    • Betrachtet nur (vergangenes) Verhalten eines Sprungs, um dessen (zukünftiges) Verhalten vorherzusagen.
    • Arbeitet rein lokal!
  • Idee: Verbesserung durch Betrachtung des Verhaltens anderer Sprünge
    • Man erhält so genannten korrelierenden Prädiktor (correlating predictor) oder zweistufigen Prädiktor
    • Prinzip: Aufgrund globaler Information (anderer Sprünge) wird einer von mehreren lokalen Prädiktoren ausgewählt

Korrelierende Prädikatoren

  • Beziehen zur Vorhersage des Verhaltens eines Sprungs Kontext-Information mit ein, d.h. die Historie anderer Sprungbefehle

  • Prädiktor benutzt globale Kontext-Bits, um einen von mehreren lokalen Prädiktoren auszuwählen

  • Betrachten wiederholte Ausführung des Codefragments (ignorieren dabei alle anderen Sprünge, inkl. dem für Wiederholung)

  • Einschränkung: Statt aller möglicher Sequenzen: d wechselt zwischen 2 und 0

Zweistufiger Prädiktor

  • Verwendet 1 Bit Kontextinformation
  • Es existieren 2 lokale Prädiktoren, beide je 1-Bit
    • Kontext: Letzter (i.a. anderer) Sprung wurde ausgeführt/nicht ausgeführt (1 Bit)
    • Vorhersage des zweistufigen Prädiktors: Anhand des Kontexts wird lokaler Prädiktor für die Vorhersage des aktuell betrachteten Sprungs ausgewählt
    • Letzter Sprung ist i.a. nicht gleich aktuellem, vorherzusagendem Sprung (nur in einfachen Schleifen)
  • Notation des Prädiktorstatus: / mit
    • : Vorhersage, falls letzter Sprung not taken, d.h. Kontext = NT
    • : Vorhersage, falls letzter Sprung taken, d.h. Kontext = T
    • und Vorhersagen: jeweils entweder T oder NT

(m,n)-Prädiktor

  • Betrachtet als Kontext das Verhalten der letzten m Sprünge, um aus 2^m vielen lokalen Prädiktoren einen n-Bit Prädiktor auszuwählen
  • Vorteil gegenüber (rein lokalem) 2-Bit Prädiktor
    • Höhere Vorhersagegenauigkeit
    • Erfordert kaum Hardwareaufwand
    • Sprunggeschichte (Kontext, „Ausgang“ vorangegangener Sprünge) kann in m-Bit Schieberegister gespeichert werden (1 Bit für jeden der m vielen letzten Sprünge im Kontext, Bit gleich 1 wenn Sprung taken)
  • Vorhersagepuffer adressiert via Konkatenation von
    • Unteren Adressbits der Sprungbefehlsadresse
    • m Bit globaler Sprunggeschichte

High Performance Befehlsdekodierung

In Hochleistungs-Pipelines ist reine Vorhersage eines Sprungs i.d.R. nicht ausreichend

  • Insbesondere: Falls mehrere Befehle pro Takt auszugeben sind
    • Befehlsstrom mit großer Bandbreite erforderlich!
    • Kontrollflussabhängigkeiten dürfen nicht „wahrnehmbar“ sein
  • Maßnahmen hierfür
    • Pufferung von Sprungzielen, und nicht nur Vorhersage des Sprungverhaltens (branch target buffer)
    • Integrierte Einheit für das Holen der Befehle (d.h. nicht nur [relativ] einfache erste Stufe der Pipeline)
    • Vorhersage von Rücksprungadressen (bei Prozeduraufruf)

Branch Target Buffer

5-stufige Pipeline, Auswertung von Sprungbedingungen in EX:

  • Branch delay von 2 Takten
  • Mit Sprungvorhersage (branch prediction buffer)
    • Zugriff erfolgt in ID (Adresse des Sprungbefehls schon in IF bekannt; aber:
    • evtl. angesprungenes Ziel erst nach Befehlsdecodierung [ID])
    • Nächste vorhergesagte Instruktion kann erst nach ID geholt werden
    • Branch delay = 1, falls Prädiktion korrekt
  • Mit Pufferung des Sprungziels (branch target buffer)
    • Zugriff auf branch target buffer erfolgt in IF. Verhalten wie „echter“ Cache,
    • adressiert mit Sprungbefehlsadresse (überprüft, ob Cache-Hit)
    • Liefert vorhergesagte Adresse als Ergebnis, d.h. nächsten PC (d.h. nicht nur Vorhersage über Sprungverhalten)
    • Keine Verzögerung, falls Prädiktion korrekt!

Zusätzliche Speicherung auch des Sprungziels, z.B. Kombination mit branch prediction buffer

Bei geschickter Organisation kann das Fließband immer gefüllt bleiben; die Sprünge kosten dann effektiv keine Zeit; CPI <1 möglich.

Eigenschaften

  • Verzögerung durch Sprung kann vollständig vermieden werden (sofern Vorhersage korrekt), da bereits in IF Entscheidung über nächsten Befehlszähler (PC) getroffen wird.
  • Da Entscheidung allein auf Basis des PC getroffen wird, muss überprüft werden, ob Adresse im Puffer (impliziert, dass Sprungbefehl vorliegt)
  • Speicherung im Prinzip nur für Sprünge notwendig, die als ausgeführt vorhergesagt werden (not taken = normale sequentielle Dekodierung geht weiter)
  • Achtung bei falscher Vorhersage
    • Entsteht ursprüngliche Sprung-Verzögerung, plus
    • Aufwand zur Aktualisierung des Vorhersagepuffers

Integrierte Befehls-Hol-Einheit (IF Unit)

Insbesondere mit Blick auf multiple-issue Prozessoren eigene (autonome) funktionale Einheit für Befehlsholphase

  • Führt Befehlscodes in Pipeline ein
  • Integrierte Funktionalitäten
    • Sprungvorhersage: Wird Teil der Befehlsholphase
    • Instruction Pre-fetch: Insbes. um mehrere Befehle pro Takt liefern (und später ausgeben) zu können, läuft Befehlsholen weiterer Dekodierung voraus (= pre-fetch)
    • Zugriff auf Befehlsspeicher: Bei mehreren Befehlen pro Takt mehrere Zugriffe erforderlich (bei Cache auf ggfs. mehrere cache lines). Werden hier koordiniert/geplant
    • Befehlspuffer: Befehle können hier (lokal im Prozessor!) von Issue-Stufe nach Bedarf abgerufen werden

Vorhersage von Rücksprungadressen

Allgemeines Ziel: Vorhersage indirekter Sprünge (d.h. bzgl. Basisadresse in Register)

  • Hauptverwendung: Rückkehr aus Prozeduraufrufen
    • MIPS: Prozeduraufruf per jal proc, Rückkehr per jr $31
    • Vorhersage mit branch target buffer schlecht, da Aufruf aus unterschiedlichen Codeteilen heraus möglich
  • Methode: (Stack-) Speicher für Rücksprungadressen
    • Push bei Prozeduraufruf (call), und
    • Pop bei Rücksprung (return)
  • Vorhersagequalität „perfekt“, wenn Stack-Puffer größer als maximale Aufruftiefe

Speicherarchitektur

Microcontroller und Digitale Signalprozessoren

Parallele Architekturen

Leistungsbewertung