Langsam nähern wir uns dem Ende des technischen Teils dieser Reihe. Das letzte mal wurde das Zweierkomplement erklärt und dargelegt, wie Subtraktion im Rechner funktioniert. Zusätzlich haben wir gesehen, wie ein einfaches Rechenwerk arbeitet; eine wichtige Grundlage der Funktionsweise sind hier die Steuersignale. Und deren Herkunft wollen wir heute klären.


Zuerst gibt es allerdings etwas Theorie.
Die Grundlage der Steuersignale sind sogenannte Instruktionen; eine Instruktion ist eine Bitkette, die eine bestimmte durch den Prozessor auszuführende Aufgabe codiert, zum Beispiel “addiere den Wert 5 auf den im Akkumulator gespeicherten Wert” (wir erinnern uns: der Akkumulator war ein spezielles Register, welcher Ergebnisse und Operanden für das Rechenwerk verwaltet). Eine Instruktion besteht in der Regel aus einem Opcode, welcher die eigentliche Operation (z.B. “addiere”) beschreibt, sowie einer optionalen Anzahl von Operanden (also etwa die zu addierenden Zahlen). Operanden können feste Werte sein, aber auch bestimmte Register beschreiben oder lediglich eine Adresse auf eine bestimmte Speicherzelle enthalten (dazu später noch etwas); die Interpretation der Operanden hängt immer vom Opcode ab. Die Länge einer Instruktion und der einzelnen Bestandteile muss übrigens nicht fest sein, sondern kann auch variabel sein, je nach zugrundeliegender Prozessorarchitektur.

Ein kurzes (fiktives) Beispiel: die Instruktion “0110110101” kann etwa in die Bestandteile 0110, 11 und 0101 unterteilt werden, wobei “0110” der Opcode ist und zum Beispiel die Anweisung “addiere einen festen Wert zu dem in einem Register gespeicherten Wert” codiert, “11” das zu benutzende Register (zum Beispiel den Akkumulator) beschreibt und “0101” schließlich den festen Wert, hier eine 5, codiert. Zusammengenommen ergibt die Bitkette also die Instruktion “addiere den Wert 5 auf den im Akkumulator gespeicherten Wert”.

Die Abarbeitung von Instruktionen durch das sogenannte Steuerwerk im Prozessor erfolgt bei der Von-Neumann-Architektur (welche die Grundlage der meisten modernen Rechner bildet) nach dem Von-Neumann-Zyklus. Dieser besteht üblicherweise aus 4 Phasen:

  • Fetch: die nächste auszuführende Instruktion wird aus dem Speicher geladen und im sogenannten Instruktionsregister gespeichert
  • Decode: die Instruktion im Instruktionsregister wird dekodiert, also grob gesagt in ihre Bestandteile “zerlegt”; konkret werden hier unter anderem die Steuersignale aus der Instruktion extrahiert
  • Fetch Operands: eventuelle Operanden aus dem Speicher werden in die entsprechenden Register geladen
  • Execute: die Anweisung wird ausgeführt

Soweit zur Theorie. Die klingt zwar immer recht schön, lässt aber offen, was nun in den einzelnen Phasen genau passiert. Schauen wir also einmal etwas genauer hinein; dazu benötigen wir noch einige neue Bauelemente, die jeweils an der entsprechenden Stelle näher beschrieben werden.

Phase 1: Fetch

Bevor eine Instruktion verarbeitet werden kann, muss sie erst einmal zur Verfügung stehen, da der Prozessor nur mit Werten arbeiten kann, die in einem seiner Register vorliegen. Jetzt ist die Anzahl der Register eines Prozessors in der Regel sehr begrenzt; es stehen auf keinen Fall genügend zur Verfügung, um viele Instruktionen (welche zusammen übrigens ein komplettes Programm ergeben!) aufzunehmen. Für die Speicherung von Instruktionen existiert im Prozessor ein separates Register (in modernen Rechnern sogar mehrere davon), welches Instruktionen speichert, das sogenannte Instruktionsregister. Die Instruktionen selbst stehen woanders, in der Regel im Cache oder im Arbeitsspeicher.

Ohne jetzt allzu sehr ins Detail zu gehen: der Cache ist ein kleiner, sehr schneller Speicher, der sich physikalisch in der Nähe des Prozessors (oder sogar in ihm) befindet und unmittelbar benötigte Daten aufnimmt. Je nach Nähe zum Prozessor werden verschiedene Cache-Level unterschieden; so haben moderne Prozessoren in der Regel einen Level-1-Cache, der direkt im Prozessor selber integriert ist, im gleichen Takt wie dieser läuft und eine Größe im Bereich von bis zu wenigen hundert Kilobyte hat. Dazu gibt es in der Regel noch mindestens einen Level-2-Cache außerhalb des Prozessors, der langsamer arbeitet und bis zu einigen Megabyte groß sein kann. Irgendwo weiter hinten in der Speicherhierarchie findet sich dann schließlich der Arbeitsspeicher, der langsamer als die Caches arbeitet, dafür aber auch bedeutend größer ist (momentan sind etwa 4 Gigabyte relativ üblich). Irgendwo in einem Speicher dieser Hierarchie steht auch die nächste Instruktion, die der Prozessor bearbeiten soll, und die gilt es zu finden. Hierfür steht ein separates Register zur Verfügung, der sogenannte Program Counter (PC). Dieser enthält die Adresse der nächsten Instruktion (in Form einer Bitkette), anhand derer die betreffende Instruktion im Speicher gefunden und ins Instruktionsregister geladen werden kann.

1 / 2 / 3 / Auf einer Seite lesen

Kommentare (12)

  1. #1 etth
    August 24, 2011

    Jetzt wo bekannt ist, wie ein Prozessor Zahlen addieren kann stellt sich mir die Frage, ob die Addition denn reicht, um alle notwendigen Operationen auszuführen. Die Division ist meines Wissens die einzige Rechenoperation, die nicht als Addition umschrieben werden kann. Wie wird diese durchgeführt? Und tun die Dinger wirklich nicht mehr als so grundsätzliche Rechenoperationen durchzuführen?

  2. #2 Marcus Frenkel
    August 24, 2011

    @etth
    Die Division mit ganzen Zahlen (also Division mit Rest) lässt sich ziemlich einfach als wiederholte Subtraktion umschreiben. Von der ersten Zahl wird die zweite einfach so lange abgezogen, bis ein Wert < 0 rauskommt - dann hat man das Ergebnis und den Rest. Etwas anders sieht es natürlich bei reellen Zahlen aus - aber vor denen habe ich mich mit voller Absicht etwas gedrückt, da insbesondere die Erklärung der Rechenoperationen hier ziemlich umfangreich werden würde. 😉 Teilweise arbeiten Prozessoren mit den grundsätzlichen Rechenoperationen, teilweise sind bestimmte Operationen aber auch anders umgesetzt; da sind schon einige Tricks möglich, die dann auch meist im Rechenwerk fest verdrahtet werden. Die Multiplikation wird zum Beispiel auch nicht einfach über wiederholtes addieren umgesetzt (was ginge, aber langsam wäre), sondern über eine Kombination aus Addition und Verschiebungen (Shiften, dafür waren die Schieberegister gut). Die Rechenoperationen beschränken sich natürlich auch mehr oder weniger auf die ALU - außerhalb davon kann der Prozessor natürlich auch noch Dinge, vor allem Daten hin- und herschicken und (de-)codieren. Aber ein Großteil der Arbeit findet schon in der ALU statt - nicht umsonst wird die Leistung eines Prozessors auch in FLOPS, also floating point operations per second (im Grunde Berechnungen) angegeben.

  3. #3 Dagger
    August 25, 2011

    Schöne Serie! Erinnert mich gut ans Studium 🙂

  4. #4 Jan von nebenan
    August 26, 2011

    Sehr schöner Artikel! Ein paar technische Sachen gäbe es vielleicht schon noch, aber ich weiß nicht, ob das nicht zu tief in die Materie geht. Ich fände z.B. interessant, mal auf die Features der neueren Prozessoren einzugehen. Die sich ja nicht nur deswegen so schnell, weil die Taktfrequenz höher ist: SIMD, Pipelining etc. wären solchen Themen, oder auch “intelligente” Funktionen wie branch prediction (Prozessor versucht zu ermitteln, ob eine If-Bedigung wahr oder falsch sein wird, und lädt entsprechende Daten vor – Trefferquote bis 98% bei modernen CPUs).

    Und eine Sache noch: Misst man die Leistung der ALU nicht in MIPS (million instructions per second)? FLOPS bezieht sich doch auf die FPU, die du ja geschickt ausgelassen hast… 😉

  5. #5 Marcus Frenkel
    August 26, 2011

    @Jan von nebenan
    Ui. Branch prediction ist natürlich ein ganz schöner Brocken. 😉
    Pipelining wäre aber in der Tat ganz interessant; ich hatte überlegt, es noch in diesem Artikel zu bringen, es dann aber auf Grund der Länge gelassen. SIMD ginge ja Richtung Prozessorarchitektur – da müssen dann auch noch SISD, MISD und MIMD erklärt werden. Ich überleg mir mal, ob das schon zu technisch ist oder nicht…oder zu magisch, da ich da auch nicht alles erklären kann und oft genug auf die Verdrahtung in der CPU verweisen muss. 😉

    Das mit den FLOPS war etwas ungeschickt ausgedrückt, ja. Man misst die Leistung einer ALU unter anderem in FLOPS (und meint dann damit die FPU darin). Die MIPS gibt es ja auch, aber die sind ähnlich aussagekräftig wie die Taktrate. 😉

    Und mit der FPU werde ich mich nur beschäftigen, wenn hier mindestens 5 Leute einen derartigen Wunsch äußern – Gleitkommawerte sind einfach zu garstig und mathematisch. 😉

  6. #6 icehawk
    August 27, 2011

    Erstmal möchte ich sagen, dass ich den Wunsch von Jan teile und auch gerne etwas über moderne Features lesen würde.

    Aber zu deiner einen Anmerkung:
    “für die technisch Versierteren: damit das funktioniert, muss die an BL/BL angelegte Spannung größer sein als die in den Transistoren gespeicherte, um den aktuellen Wert zu überschreiben”
    Das verwirrt mich etwas: Braucht man dann nicht mit der Zeit immer höhere Spannungen? Bei mehreren Ghz kommen ja schon ein paar Überschreibvorgänge zusammen: Wann geht der CPU der Saft aus? ^^

  7. #7 Marcus Frenkel
    August 27, 2011

    @icehawk
    Das Wort “gespeichert” war hier etwas unglücklich gewählt – ein Transistor speichert natürlich keine Spannung, das wäre ein wenig schwierig. Aber die beiden Transistor-Paare in der Mitte “speichern” ein Bit dadurch, dass sie sich in einem von zwei sogenannten stabilen Zuständen befinden, was die Verteilung der Leitfähigkeit und damit Spannung angeht – es reicht übrigens eine sehr niedrige Spannung, um so einen Zustand aufrecht zu erhalten. Zum Überschreiben muss kurzfristig eine stärkere Spannung angelegt werden, um die bisherige zu “überschreiben” – aber die stärkere Spannung bleibt nicht in den Transistoren bestehen. Somit braucht man auch nicht immer höhere Spannungen.

  8. #8 Engywuck
    August 31, 2011

    och, gegen FPU in leicht verdaulicher Form hätte ich nichts einzuwenden 😉 Addieren kann ja jeder ^^

    Zum Thema “Multiplizieren über Addition”: mein Opa hatte einen mechanischen Tischrechner (mit Motor) mit den drei Grundrechenarten: Addition, Subtraktion, Multiplikation (Division ist ja nicht einfach….). Damit habe ich als Kind gerne gespielt 🙂
    Man überlegt sich dann aber recht schnell, ob man “4 * 50” oder “50 * 4” berechnet. In ersterem Fall wird 4 – plus 4 – plus 4 – plus 4 …. gerechnet. Das Ganze mit Höllenlärm wegen der Mechanik samt Motor und entsprechendem Zeitaufwand (so 1-2 Operationen pro Sekunde….).
    Angeschafft wurde das Teil damals übrigens von der Milchgenossenschaft im Ort, weil mein Opa als “Zahlstelle” sich geweigert hat, die Auszahlungen von Hand zu berechnen. Bei seinem Vorgänger hatten die Mitglieder sich immer beschwert, er habe falsch gerechnet, mit der Maschine haben sie’s dann einfach geglaubt… 😉

  9. #9 Marcus Frenkel
    August 31, 2011

    Ja ja, die lieben Optimierungen bei der Befehlsausführung. Die gibt es ja immer noch – nur in hochkomplexer Form. 😉

    Ok, da sich nun einige was zu Gleitkommazahlen gewünscht haben, werde ich dazu wohl auch noch irgendwann einen Artikel bringen, in der Hoffnung, damit nicht die letzten Leser zu vergraulen. 😉

  10. #10 rolak
    September 4, 2011

    Hi etth, mit einer den üblichen Divisions-Algorithmen angemessenen Verzögerung: Da Fließkommazahlen letztendlich nur ganze Zahlen sind, läßt sich auch deren Division auf add/sub reduzieren. Es gibt meines Wissens nur einen wesentlichen Unterschied: Liegt ein HW-Multiplizierer für n-bit-Zahlen vor, kann mit minimalem Aufwand ein Algorithmus für k*n-bit-Zahlen implementiert werden (k² zu addierende Zwischenergebnisse) während einem ein HW-Dividierer diese Möglichkeit nicht bietet.
    In der Praxis werden jedoch schnellere Techniken genutzt (SRT, N/R, Goldschmidt) — was dann vielleicht schon erste Vorschläge für den FPU-Artikel wären, da ansonsten doch exakt genauso gerechnet wird wie vom schriftlichen multiplizieren/dividieren gewohnt. Nur halt im Binärsystem.

    Branch predicition ist doch, wenn auch wesentlich für Tempo und effiziente Pipeline-Auslastung, ‘nur’ eine Kombination aus statistischer Auswertung (zB saturating counter) und Schmierzettel (zB BTB).
    Beim Z80 war die Ausführungszeit ja noch fest – doch auch in diesem Zusammenhang hatte ich ziemliche Schwierigkeiten, einen Programmierer davon zu überzeugen, daß ~150 gesparte Takte bei dem 1aus256-Sonderfall die je 2 Takte für den nicht ausgeführten bedingten Sprung in keiner Weise aufwiegen. Auch eine Art von branch prediction 😉

    Du wirst schon keinen vergraulen, Marcus. Schlimmstenfalls werden einige posts seltener gelesen als andere(wenn schon nicht seltener geklickt), doch das dürfte bei noch so sorgfältiger Vorabfilterung eigentlich bei keinem Themenkomplex zu vermeiden sein.

  11. #11 Michi
    September 23, 2011

    Vielen vielen Dank! Die Serie hat mir viel geholfen!

  12. #12 ilikepepsi
    September 26, 2011

    @Marcus:

    Hi, hast du vor in der “Programmierung von Computern” Reihe die Funktionsweise von Compilern näher zu beleuchten? Falls nicht, das fände ich sehr interessant.