Einführung in die Systemprogrammierung 02 Prof. Dr. Christoph Reichenbach Fachbereich 12 / Institut für Informatik 4. Mai 2014
Eine Aufgabe aus der Praxis Gegeben ein bestimmtes Programm: Machen Sie dieses Programm schneller. Neue Hardware? Neuer Compiler? Neues Betriebssystem? Neues Laufzeitsystem? Optimierungen? Neue Hardware + Installation kostet oft weniger als Entwicklerzeit
Speedup Definition: speedup(a, B) = Benötigte Zeit für B Benötigte Zeit für A Beispiel: Festgesetzte Aufgabe System A benötigt 1h System B benötigt 2h Speedup von System A über System B ist 2. Speedup gibt an, wieviele Probleme A in der Zeit lösen kann, in der B eines löst.
Auswahl neuerer Hardware Prozessor RegBit Kerne ISA Taktfrq. IBM Power7+ 64 8 PowerPC 4,2 GHz IBM zec12 64 6(?) z/arch. 5,5 GHz Oracle SPARC T5 64 16 SPARC V9 3,6 GHz AMD Piledriver FX-9590 64 8 x86/64 4,7 GHz Intel Xeon E7-8890 v2 64 15 x86/64 2,8 GHz Intel Xeon Phi 7120X 64 61 x86/64 1,2 GHz Samsung Exynos 5422 32 4 ARM 2,1 Ghz Qualcomm Snapdragon 805 32 4 ARM 2,7 Ghz Godson-3B (2011) 64 16 MIPS64 1,0 GHz ISA = Instruction Set Architecture = Befehlssatzarchitektur Viele Unterscheidungskriterien!
Wie vergleichen wir die Prozessoren? Registergröße? Anzahl der Kerne? Befehlssatzarchitektur? Taktfrequenz? Andere Prozessoreigenschaften? Andere Eigenschaften des Systems? Meist sind viele Aspekte relevant
Mehr Prozessorkerne? Mehrere Berechnungen gleichzeitig: Jeder Kern hat eigene Register Kerne verwenden gleichen Speicher Programme müssen meist umgeschrieben werden, um zusätzliche Kerne zu nutzen
Prozessorkerne: Positives Beispiel Beispiel: Web-Suchmaschine Suchbegriffe sortierte Ergebnisse Ein Prozessorkern: Webseiten Ergebnisse Vier Prozessorkerne: Webseiten (1/4) Ergebnisse (1/4) Webseiten (2/4) Ergebnisse (2/4) Webseiten (3/4) Ergebnisse (3/4) Webseiten (4/4) Ergebnisse (4/4) Ergebnisse müssen noch kombiniert werden, aber das ist relativ wenig Aufwand.
Prozessorkerne: Negatives Beispiel Element in sortiertem Binärbaum suchen Pfad durch Wurzelknoten deterministisch vorgegeben: selbst mit Programm-Modifikationen schwer zu parallelisieren Zusätzliche Kerne sind hier u.u. nutzlos.
Höhere Taktfrequenz? Taktfrequenz ist die Anzahl der Taktzyklen pro Sekunde Jeder Maschinenbefehl benötigt eine bestimmte Anzahl von Taktzyklen Meist schnell: Logik, einfache Arithmetik Meist langsam: Division, Speicherzugriff Beispiel Blinkanlage: Annahme: 1 MHz-Prozessor Annahme: Jeder Befehl benötigt einen Taktzyklus Blinkprogramm 10 (und Wiederholung) Lampe wird 500.000 Mal pro Sekunde an- und ausgeschaltet
Höhere Taktfrequenz? Nicht alle Operationen laufen in einem Taktzyklus ab. Beispiel SGI MIPS R10000 (1992): Operation Zyklen (32 Bit) Zyklen (64 Bit) Ganzzahl-Multiplikation 6 10 Ganze Zahl laden 2 2 Fließkommazahl laden 3 3 Fließkomma-Addition 2 2 Fließkomma-Multiplikation 2 2 Fließkomma-Division 11 18 Fließkomma-Quadratwurzel 17 32
Taktfrequenz, Instruktionen, Zyklen Befehle benötigen mehrere Taktzyklen Länge eines Zyklus: 1 Taktfrequenz Kennwerte zur Analyse: CPI: Zyklen pro Instruktion (kleiner schneller) IPC: Instruktionen pro Zyklus (größer schneller) CPI = Zyklen Instruktionen IPC = Instruktionen Zyklen Moderne Prozessoren verfügen über Spezialbefehle, um Zyklen und Instruktionen zu messen
Taktfrequenz: Positives Beispiel Suche nach größtem gemeinsamem Teiler (ggt): { x x y, y mod x = 0 ggt(x, y) = ggt(x, z) x < y, (y mod x) = z, z > 0 Standardalgorithmus hängt nur von Prozessorgeschwindigkeit ab Eher seltene Situation in der Praxis
Taktfrequenz: Negatives Beispiel Webserver für statische Informationen: Erhält Anfrage aus Netzwerk Durchsucht Festplatte nach passender Datei Schickt Antwort über Netzwerk Aufwand primär bei: Festplatte Netzwerk Verbindung Prozessor/Geräte Prozessor nur periphär beteiligt
Andere Befehlssätze? Unterschiedliche Prozessoren haben verschiedene Befehlssatzarchitekturen: Speicherzugriffsmodus Adressierungsmodi Operandengrößen und -typen Operationen Kodierung der Operationen
Speicherzugriffsmodus Register/Speicher-Architektur Keine speziellen Ladebefehle Operationen können Speicher direkt adressieren Lade-Schreib-Architektur Strikt getrennt: Speicherbefehle Ladebefehle Andere Befehle mov $23, %eax (EAX := 23) add [%ebx], %eax (EAX := EAX +mem32[ebx]) x86, m68k, IBM 360, VAX uldl t0, 0(t2) (t0 := mem32[t2]) addl t0, 23, t0 (t0 := t0 + 23) Alpha, MIPS, PowerPC, SPARC, ARM
Adressierungsmodi Erlaubte Operanden in Instruktionen: Register ($t0,... ) add $s0, $t1, $t2 (MIPS) Direktoperanden (1, 2, 3,... ) addi $t0, $t0, 23 (MIPS) Speicheroperanden (mem32[%eax],... ) add [%ebx], %eax (x86) Distanzadressen: lw $t0, 0x100($t0) (MIPS, $t0 := mem32[0x100 + $t0]) mov 0x8(%edx, %ebx, 4), %eax (x86, %eax := mem32[0x8 + %edx + %ebx 4])
Operandengrößen und -Typen Operandengrößen: 4, 8, 9, 16, 24, 32, 36, 40, 64, 128 Bit Zahlen ohne Vorzeichen Zweierkomplementzahlen Einerkomplementzahlen IEEE 754-Fließkommazahlen VAX-Fließkommazahlen...
Operationen Speicher/Ladebefehle Sprung-/Verzweigungsbefehle Systembefehle Arithmetische und logische Befehle Spezialbefehle: Fließkommabefehle Vektorbefehle Präfixbefehle: REP MOVSB (x86) Wiederhole MOVSB eine bestimmte Zahl von Iterationen Bedingte Befehle (32-Bit ARM): MOVVS r0, r1 Kopiere r1 nach r0 falls letzte Arithmetikoperation einen Überlauf verursachte Sehr große Instruktionsworte (Very Large Instruction Words, VLIW) (IA-64)
Kodierung der Operationen user@host:~$ hexdump -C hallo-welt.o... 0200 66 b8 01 00 66 bf 01 00 48 be 00 be 00 00 00 00 f...f...h... 0210 00 00 ba 11 00 00 00 0f 05 b8 3c 00 00 00 bf 00...<... 0220 00 00 00 0f 05 00 00 00 00 00 00 00 00 00 00 00... 0230 48 61 6c 6c 6f 2c 20 57 65 6c 74 21 0a 00 e8 03 Hallo, Welt!... 0240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00...... Maschinensprache Maschinensprache Assembler (x86/64) 66 b8 01 00 mov 0x1,%ax 66 bf 01 00 mov 0x1,%di 48 be d8 00 60 00 00 00 00 00 movabs 0x6000d8,%rsi ba 11 00 00 00 mov 0x11,%edx 0f 05 syscall b8 3c 00 00 00 mov 0x3c,%eax bf 00 00 00 00 mov 0x0,%edi 0f 05 syscall Manche Befehlssätze haben Operationen mit variabler Länge
Befehlssätze: Positives Beispiel Numerische Simulationen: Naturwissenschaften: Simuliere Verhalten von physikalischen / chemischen / biologischen Systemen Basiert auf ungenauen Kommazahlen Fließkommaoperationen: Hardwareunterstützung zu diesem Zweck Schneller als Software-Emulation von Kommazahlen
Befehlssätze: Negatives Beispiel LISP-Maschine: dediziertes System, um LISP-Programme auszuführen Spezialisierte Unterstützung für: LISP-Typanalyse Automatische Speicherverwaltung Mehrere Generationen der Maschine Inzwischen entwickelten sich Allzweckrechner schneller: Größerer Markt, mehr Investitionen, mehr Wettbewerb Allzweckrechner holten LISP-Maschine ein
Andere Rechnerkomponenten DRAM-Speicher Flash-Speicher Festplatte Netzwerk Grafikchip Performanz realistischer Systeme hängt oft wesentlich von den Komponenten / Peripheriegeräten ab
Zusammenfassung: Performanz Hängt von vielen Faktoren ab Eigenschaften des Prozessors: Ohne Softwareunterstützung: Taktfrequenz Aber: Vorsicht vor dem Megahertz-Mythos : schneller getakteter Prozessor muß nicht schneller sein! IPC / CPI beachten! Mit Softwareunterstützung Zusätzliche Kerne Spezial-Befehlssätze Andere Komponenten
Ahmdahls Gesetz Wert einer einzelnen Optimierung ist Speedup speedup = Laufzeit ohne Optimierung Laufzeit mit Optimierung Annahme: Eine-Komponente (Hardware/Software) nimmt k% der Gesamtlaufzeit Maximaler Speedup durch Optimierung: max. speedup = Laufzeit ohne Optimierung Laufzeit ohne Optimierungen * (1-k) = 1 1 k z.b.: Komponente braucht 20% Gesamtzeit, max. Speedup ist 1,25 Messen Sie die Wichtigkeit einer Komponente, bevor Sie optimieren!
Geschwindigkeit und Taktfrequenz 100000 10000 1000 Transistoren MHz SPECint-Performanz 1000 100 10 1 1978 1982 1986 1990 1994 1998 2002 2006 2010 Performanz wächst schneller als Taktfrequenz
Gründe für die Beschleunigung Mooresches Gesetz: Alle 18-24 Monaten verdoppelt sich die Anzahl der Transistoren in integrierten Schaltkreisen Verwendung der neuen Transistoren: Pipelining Superskalare Befehlsausführung Caches (nächste Vorlesung) Ausführung außer der Reihe (out-of-order execution) Spekulative Ausführung
Pipeline-Ausführung Prozessor besteht aus mehreren Komponenten Komponenten werden nacheinander verwendet Beispiel hier: Einfacher MIPS-Prozessor PSp Reg ALU DSp Reg
Pipeline-Ausführung: Protagonisten PSp IL Instruktion laden Reg ID Instruktion dekodieren ALU AUS Arithmetische und logische Komponenten der Instruktion ausführen DSp DS Datenspeicherzugriff Reg RS Register mit Ergebnissen beschreiben
Pipeline-Ausführung: IL Instruktion laden Nächstes Programmwort (gemäß Programmzähler) aus Programmspeicher laden Programmzähler um 4 erhöhen PSp
Pipeline-Ausführung: ID Instruktion dekodieren Instruktion entschlüsseln Benötigte Registerinhalte laden (Register sind bei MIPS an festen Adressen im Maschinensprachebefehl) Vorzeichenerweiterung des Direktoperanden Geladene Register auf Gleichheit prüfen (für Sprung) Reg
Pipeline-Ausführung: AUS Instruktion ausführen (arithmetisch/logisch) Verknüpfung der Operatoren aus ID (Direktoperanden, Registerinhalte) Arithmetik z.b. auch Distanzadressen (lw 0x200($t3)) addieren Logische Operationen ALU: Arithmetisch-Logische Einheit (arithmetic-logic unit) ALU
Pipeline-Ausführung: DS Datenspeicher-Zugriff Berechnete Adresse verwenden: Ladebefehl: Aus Datenspeicher lesen Schreibbefehl: In Datenspeicher schreiben DSp
Pipeline-Ausführung: RS Rückschreiben in die Register Ergebnis zurück in Zielregister schreiben Reg
Traditionelle Ausführung [0000] addi $t0, $t0, 7 [0004] lw $t1, 0($s0) IL ID AUS DS RS PSp Reg ALU DSp Reg t=0 [0000] t=1 [0000] t=2 [0000] t=3 [0000] t=4 t=5 [0004] t=6 [0004] t=7 [0004] t=8 [0004] [0000]
Ausführung mit Pipelining [0000] addi $t0, $t0, 7 [0004] lw $t1, 0($s0) [000C] lw $t2, 4($s0) [0008] mul $s2, $s3, $s3 [0010] j loop IL ID AUS DS RS t=0 [0000] t=1 [0004] [0000] t=2 [0008] [0004] [0000] t=3 [000C] [0008] [0004] [0000] t=4 [0010] [000C] [0008] [0004] [0000] t=5 [0010] [000C] [0008] [0004] Effiziente Ausnutzung: Theoretisch Speedup bis zu 5 Praktisch: Weniger; schnellere Komponenten warten auf langsamere
Speicherarchitektur mit Pipelining Gleichzeitiger Zugriff auf Daten- und Programmspeicher: Benötigt Harvard-Architektur Moderne Rechner benötigen Flexibilität der von-neumann-architektur Lösung: modifizierte Harvard-Architektur: Gemeinsamer Hauptspeicher wird durch zwei separate Kanäle an Prozessor angebunden
Hazards [0000] addi $t0, $t0, 1 [0004] add $t1, $t0, $t1 Pipelining nicht möglich: [0004] benötigt $t0 nach [0000]! Hazard: Pipeline-parallele Ausführung durch Abhängigkeiten blockiert RAW hazard (read after write): Lesebefehl muß auf Schreibbefehl warten (s.o.) WAR hazard (write after read): Schreibbefehl muß auf Lesebefehl warten (Nur bei Systemen mit Schreibkomponente vor Lesekomponente) WAW hazard (write after write): Schreibbefehl muß auf Schreibbefehl warten (Nur bei Systemen mit mehreren Schreibe-Komponenten)
Pipeline-Blasen [0000] addi $t0, $t0, 1 [0004] add $t1, $t0, $t1 [000C] lw $t2, 0($s0) [000C] lw $t3, 4($s0) [0010] j loop IL ID AUS DS RS t=0 [0000] t=1 [0004] [0000] t=2 [0008] [0004] [0000] t=3 [0008] [0004] [0000] Änderung an $t0 wird in 0004 gelesen: Müssen warten! t=4 [0008] [0004] [0000] RS schreibt in 1. Hälfte des Zyklus, ID liest in 2. Hälfte t=5 [000C] [0008] [0004] t=6 [0010] [000C] [0008] [0004] t=7 [0010] [000C] [0008] [0004]
Durchreichen von Registern [0000] addi $t0, $t0, 1 [0004] add $t1, $t0, $t1 [000C] lw $t2, 0($s0) [000C] lw $t3, 4($s0) [0010] j loop Zwischenregister: Können Zwischenergebnisse durchreichen Durchreichen PSp Reg ALU DSp Reg IL ID AUS DS RS Keine Pipeline-Blase in diesem Beispiel!
Durchreichen von Registern: Grenzen Auch mit Durchreichen Verzögerungen möglich Beispiel: ALU hängt von Speicher ab [0000] lw $t0, 0($s0) [0004] add $t1, $t0, $t1 Durchreichen PSp Reg ALU DSp Reg IL ID AUS DS RS
Zusammenfassung: Daten-Hazards Daten-Abhängigkeiten zwischen Befehlen können zu Blasen in der Pipeline führen Teilweise Lösung durch: Zwischenregister Durchreichen von Daten Arten von Daten-Hazards: RAW, WAR, WAW Reihenfolge von Instruktionen hat oft Einfluß auf Performanz
Verzweigungen und Pipelining [0000] lw $t1, 0($s0) [0004] beqz $t0, ziel [0008] lw $t2, 4($s0) [000C] lw $t3, 8($s0) ziel: [0010] lw $t4, 12($s0) PSp Reg ALU DSp Reg IL ID AUS DS RS Was, wenn wir den Sprung nehmen?
Verzweigungen und Pipelining [0000] lw $t1, 0($s0) [0004] beqz $t0, ziel [0008] lw $t2, 4($s0) [000C] lw $t3, 8($s0) ziel: [0010] lw $t4, 12($s0) PSp Reg ALU DSp Reg IL ID AUS DS RS Operation [0008] wird geladen (IL), aber gleichzeitig stellt Operation [0004] in ID fest, daß wir springen müssen. Wir invalidieren Opertion Operation [0008], was eine Pipeline-Blase erzeugt. Blase durch Sprung!
Sprünge in der Pipeline Der Programmzähler wird in IL erhöht Sprung kann frühestens in ID stattfinden Wir haben die Wahl: Annehmen, daß wir den Sprung nicht nehmen: IL wie bisher von PC+4 Annehmen, daß der Sprung nehmen: IL von Sprungzieladresse Beides gibt einen Zyklus Blase, wenn wir falsch annehmen Mehrere Verbesserungen möglich: Statische Verzweigungsvorhersage Verzögerte Verzweigung Dynamische Verzweigungsvorhersage
Statische Verzweigungsvorhersage Hochsprachen-Übersetzer sagt voraus, ob Sprung stattfindet oder nicht Alle Verzweigungsoperationen haben zwei Varianten: Warhscheinlich (likely, z.b. bgtzl auf MIPS R4000) Unwahrscheinlich (z.b. bgtz auf MIPS R4000) Weniger effektiv als Alternativen
Verzögerte Verzweigung Änderung der Bedeutung der Verzweigungs- und Sprungbefehle: Der Befehl unmittelbar nach einem Verzweigungs-/Sprungbefehl wird immer ausgeführt. Beispiel: MIPS R10000 Vorteil: Effizient und einfach Nachteil: Nicht immer möglich, müssen evtl. nop (no-op, Operation ohne Wirkung) einführen [0000] lw $t1, 0($s0) [0004] beqz $t0, ziel [0008] lw $t2, 4($s0) [000C] lw $t3, 8($s0) ziel: [0010] lw $t4, 12($s0) Mit verzögerter Verzweigung wird die Operation an Adresse 0008 immer ausgeführt, egal ob die Verzweigung stattfindet oer nicht
Dynamische Verzweigungsvorhersage Prozessor erweitert um eine Verzweigungsvorhersagetabelle Tabelle merkt sich wenige (z.b. 4096) Einträge mit: (Teil der) Adresse des Sprungbefehls Wurde der Sprung genommen? Bei nächstem Sprung: letztes Ergebnis aus Tabelle verwenden Nachteil: Sprunghäufigkeit wird nicht berücksichtigt In der Praxis problematisch bei verschachtelten Schleifen
Dynamische Verzweigungsvorhersage: 2 Bit Heute übliches Verfahren: 2-Bit Vorhersage genommen 11 Vorhersage: Nehmen nicht genommen genommen 10 Vorhersage: Nehmen genommen nicht genommen 01 Vorhersage: Nicht Nehmen nicht genommen genommen 00 Vorhersage: Nicht Nehmen nicht genommen
Zusammenfassung: Kontrollfluß-Hazards Nicht statisch vorhersehbar, ob Befehle unmittelbar hinter Verzweigungen ausgeführt werden oder nicht Pipeline-Blase bei Fehlabschätzung Reduzierung der Verzweigungskosten: Statische Verzweigungsvorhersage Verzögerte Verzweigung Dynamische Verzweigungsvorhersage
Superskalare Ausführung Mehrere parallele Pipelines Zwischenregister reichen zwischen Pipelines hin und her [0000] [0004] [000C] [0008] Pipeline #0 Pipeline #1
Zusammenfassung: Pipelining und Hazards Pipelining (auch ILP, Instruction-Level Parallelism) kann mehrere Befehle gleichzeitig ausführen Mehrere Befehle in unterschiedlichen Phasen einer Pipeline möglich Mehrere Pipelines parallel: Superskalare Ausführung Hazards: Daten-Hazards: Datenabhängigkeiten zwischen Registern Kontrollfluß-Hazards: Unvorhersehbare Sprünge Strukturelle Hazards: Komponenten, die nicht auf Pipeline-Architektur ausgelegt sind
Nächste Woche: Die Speicherhierarchie / Speicherfehler