Die CPU ist als der zentrale Baustein praktisch aller Computer bekannt.
Nachdem wir schon einen kleinen Blick in ihren Speicher geworfen haben, soll es heute darum gehen, wie dieser Baustein dazu gebracht wird, das Richtige zur rechten Zeit zu tun.
Shownotes
Rückblick: Logikgatter (STP003)
aus Gattern lassen sich Schaltkreise bilden, die konkrete Einzelberechnungen anstellenhauptsächlich einfache Rechenoperationen wie "zwei 16-Bit-Zahlen addieren"aber auch teils sehr komplexe Einzeloperationen, z.B. Teilschritte einer bestimmten Hashfunktion (siehe STP004) oder eines bestimmten Audio/Video-Codecs (heißt in Produktbeschreibungen meist Hardware-Beschleunigung)Frage 1: Wie kann man mehrere Operationen nacheinander ausführen?Frage 2: Wie kann man in einem einzelnen Taktschritt aus verschiedenen möglichen Operationen wählen?Frage 3: Wie entsteht daraus ein ganzes Programm, also eine bestimmte Folge von verschiedenen Operationen?Antwort auf Frage 1: Taktgenerator
analog zum Taktgeber in einer Uhr (Unruh bei mechanischen Uhren, Quartzoszillator bei elektronischen Uhren), aber als mikroeletronische Schaltung realisiertGeschwindigkeitsvergleich: bei Quartzuhren hat der Oszillator meist 32768 Hzheutzutage ist die Taktfrequenz meistens regelbar1990er: Turbo-Schalter am Gehäuse zum Verdoppeln (bzw. in der anderen Richtung Halbieren) der Taktfrequenzseit den 2000ern: ACPI und Co., Energiesparen durch bedarfsgerechtes Heruntertakten der CPU-KerneHeruntertakten als Schutz vor Überhitzung (nach Überlastung oder beim Versagen der Kühlung)unter Linux: aktuelle Taktfrequenz auslesen mittels grep '^cpu MHz' /proc/cpuinfoAntwort auf Frage 2: Arithmetisch-logische Einheit (ALU)
Idee: neben 2N Eingängen für zwei N-Bit-Eingabezahlen noch weitere Eingänge, die einen Opcode darstellen, also eine fortlaufende Nummer für die gewünschte RechenoperationOpcode bestimmt, von welchem Teilschaltkreis das Ergebnis nach außen durchgereicht wirdhierzu zusätzliche Logikschaltung am Ende: Ausgabe = ((Opcode = 1) AND Ausgabe1) OR ((Opcode = 2) AND Ausgabe2) OR ...analog zur ALU gibt es meist noch eine FPU für Fließkommazahl-OperationenAntwort auf Frage 3: Steuerwerk in der CPU (oder GPU)
Programmdatei besteht aus einer Folge von Befehlen, die im Arbeitsspeicher vorliegenSteuerwerk hat einen Befehlszeiger (Instruction Pointer), der auf den jeweils nächsten Befehl zeigtBefehle sind in Maschinensprache kodiert
meist 16/32/64 Bit groß (je nach Prozessorarchitektur)für Rechenbefehle wie "Addition" oder "Multiplikation"den Opcode (welche Berechnung auszuführen ist)aus welchen Prozessorregister die Eingabe kommt (evtl. ist ein Eingabewert eine Konstante wie "0" oder "1" und steckt direkt im Befehl drin)in welches Prozessorregister die Ausgabe gehtandere Befehlsarten:Load: aus dem Hauptspeicher einen Wert in ein Prozessorregister holen (Parameter: Quelladresse und Zielregister)Store: genau andersherum (Parameter: Quellregister und Zieladresse)Compare: schickt zwei Zahlen aus Registern an die ALU, aber statt einer Ergebniszahl gibt es einen Satz von Flags wie "A ist gleich B" oder "A ist kleiner als B" zurückJump: aktualisiert den Befehlszeiger, evtl. nur wenn ein bestimmtes Flag gesetzt ist (zum Beispiel Gleichheits-Flag aus einem vorherigen Compare oder Überlauf-Flag aus einer vorherigen Addition)Call/Return: Call macht einen Sprung, aber speichert vorher die aktuelle Position in einen designierten Teil des Arbeitsspeichers; Return springt an den Ursprung des letzten Call zurück (damit verschachtelbare Unterprogramme realisierbar)Ausführung eines Befehls ist unterteilt in mehrere Schritte
grobe Skizze der Abarbeitung eines einzelnen Befehls (Schritt 2-5 jeweils nur, sofern für den entsprechenden Befehl zutreffend)Schritt 1: Auslesen und Dekodieren des BefehlsSchritt 2: Laden der Eingabewerte aus den entsprechenden RegisternSchritt 3: Ausführen des Befehls (z.B. bei Rechenbefehlen mittels der ALU oder FPU)Schritt 4: Speichern des Ausgabewertes im entsprechenden ZielregisterSchritt 5: Verschieben des Befehlszeigers auf den Folgebefehlin der Praxis meist noch viel mehr Teilschritte (z.B. moderne x86-CPUs: über 40 Pipeline-Schritte)Pipelining: wenn ein Befehl einen bestimmten Schritt durchlaufen hat, kann hier meist schon mit dem nächsten Befehl begonnen werden, auch wenn der vorherige Befehl noch nicht komplett abgearbeitet warspekulative Ausführung (Speculative Execution): nachfolgende Befehle rechnen mit den Daten, von denen die CPU glaubt, dass sie wahrscheinlich aktuell sein werden, sobald derjenige Befehl tatsächlich an der Reihe istSprungvorhersage (Branch Prediction): bei einem Sprungbefehl mit Vorbedingung muss die CPU möglichst gut abschätzen, ob die Vorbedingung eintritt und der Sprung vollzogen wird, um die nächsten Befehle vorhersagen zu könnenVerbesserungen in spekulativer Ausführung und Sprungvorhersage sind einer der Hauptgründe, warum Desktop-CPUs auch ohne höhere Taktfrequenzen immer schneller geworden sindÜberlegungen für schnelle Programme
Sprungbefehle sind für schnelle Programme ein Problem: wenn die CPU einen Sprung falsch vorhersagt, muss nach dem Ausführen des Sprungs die gesamte Pipeline verworfen und neu gefüllt werdenverschiedene Befehle dauern unterschiedlich lange in der Berechnung (z.B. Multiplikation ist langsamer als Addition ist langsamer als Bit-Shift); entsprechende Austauschungen finden in Compilern meist automatisch statt, z.B. 2 * x -> x + x oder 8 * x -> x << 3