Home | Computer | Hardware | Site Map |
Heute sind Prozessoren ganz schön komplex, mit allerlei Features. Das alles erschwert aber das Verständnis der Funktionsweise eines Prozessors. In diesem Beitrag soll gezeigt werden, wie sich die Prozessorarchitektur seit den 8 Bit Tagen entwickelt hat. Dabei geht es um die Erklärung der hübschen technischen Begriffe wie "Trace Execution Cache", nicht darum ob nun Prozessor × oder y der bessere sei. Ich habe mich in der Baureihe auf die Intel Prozessoren des X86 Serie gestützt, da ich diese sehr gut intern kenne, es sind aber auch verweise auf andere Architekturen enthalten, sofern diese angebracht sind. (Hinweis: dieser Artikel stammt aus dem Jahr 2000)
Sinn macht es die Funktionsweise evolutionär zu betrachten, das heißt ausgehend von einem 8 Bit Mikroprozessor, wie er 1974-1980 Stand der Technik war die Weiterentwicklung zu untersuchen. Jeder Prozessor besteht aus mehreren Funktionseinheiten die sich seit den 8 Bit Tagen des 8080 gehalten haben:
In der grauen Computervorzeit (den sechziger und siebziger Jahren) hat man die Leistung eines Computer in Maschinenzyklen gemessen. Dieser Wert ist heute nicht mehr so von Belang, doch er ist wesentlich für das Verstehen der Funktionsweise eines Prozessors.
Die Ausführung eines Befehles geht nicht in einem Rutsch, sondern es sind mindestens 4 Phasen notwendig, die sich je nach Befehl in weitere Unterphasen aufteilen lassen.
Jeder Prozessor hat eine Anzahl sehr einfacher Befehle die typisch für ihn sind und nicht auf andere Prozessoren übertragbar. So gibt es nicht eine Maschinensprache sondern eine für Power PC, eine für Pentium und eine für Alpha Prozessoren.
Verknüpft ist diese Maschinensprache mit der Architektur des Prozessors und diese spiegelt sich auch in den Registern wieder. Stellen wir uns vor, wie würden einen neuen 16 Bit Prozessor bauen der 16 MB (24 Bit) Adressieren kann. So ein Prozessor hat typischerweise um die 60-200 Maschinenbefehle. Nehmen wir mal an es wären 64. Diese könnten wir bequem in einem Byte dekodieren, das ja 256 Zustände einnehmen kann. Doch so einfach ist es nicht. Viele Befehle benötigen einen oder sogar zwei Operanden. Hier ein Beispiel:
Wir stellen schnell fest, bei nur einem Register gibt es für jeden Befehl schon 3 Möglichkeiten die ein Operand haben kann: Ein Register, eine Konstante und eine Adresse. Bei Zwei Operanden sind es schon 6 Kombinationsmöglichkeiten. Damit vervielfältigt sich die Anzahl der Befehle.
Selbst wenn nicht alle Befehle Operanden haben, und man nicht alle Kombinationen zulässt gibt es ohne Probleme bei 3 oder 4 Registern - und dass ist nicht gerade viel - schon mehr Kombinationsmöglichkeiten als in ein Byte hineinpassen. Man benötigt also dann zwei Bytes für einen Befehl. Damit wird zum einen der Code länger, zum anderen dauert das Dekodieren von 2 Byte natürlich länger als von einem Byte.
Es gibt nun zwei Ansätze dieses Dilemma zu lösen. Beim CISC Ansatz (Complex Instruction Set Computer) versucht man mit weniger Registern auszukommen dafür mächtige Befehle zu benutzen und oft spezialisierte Register. Es gibt dies schon bei 8 Bittern wie dem Z80 der mit einem Befehl einen ganzen Speicherblock verschieben kann. Auch der 8086 und seine Nachfolger haben nur wenige freie allgemein nutzbare Register (AX,BX,CX und DX) die spezialisiert sind. Selten gebrauchte Operationen versucht man in 2 Byte zu kodieren, die häufigen dagegen in einem. Die Länge des Codes ist damit variabel und schwankte schon bei einem 8 Bit System zwischen einem und 5 Byte.
Bei RISC sagt man sich dagegen: In Ordnung, ich kann nicht alle Befehle in einem Byte kodieren, dann versuche ich wenigstens die Möglichkeiten mit 2 Byte voll auszunützen indem ich das zusätzliche Byte nutze um erheblich mehr Register anzusprechen. RISC Prozessoren haben daher oft sehr viele Register auf dem Chip - 32,64 oder gar 128 und alle sind gleichberechtigt. Es gibt nur die einfacheren Maschinenbefehle, nicht die Superbefehle von CISC. Den Nachteil von längerem Code versucht man mit einem einheitlichen Format zu begegnen. Bei unserem Papierprozessor z.B. könnte man sagen: Alle Befehle die mit Werten oder Adressen zu tun haben werden in einem Byte kodiert, die nächsten 3 Byte entfallen dann auf die Adresse/Daten und alle anderen Befehle werden in 2 Byte kodiert, dann entfallen die Bytes 3 und 4 auf jeweils die Nummer von einem von 256 Registern. Alle Befehle wären dann 32 Bits lang und man könnte 256 Register nutzen. Dafür verschwendet man Platz, denn bei CISC gibt es variable Befehlslängen, z.B. benötigt nicht jeder Befehle zwei Operanden, so das bei manchen Befehlen das 4 Byte oft unbenutzt wäre.
RISC entstand Mitte der achtziger Jahre als Bewegung aus zahlreichen Universitätsinstituten heraus und wurde von Hardwareherstellern aufgegriffen welche die Chance sahen in der Geschwindigkeit mit den Intel Prozessoren mitzuhalten, ohne über die finanziellen Mittel zur Verfügen um die gleichen Komplexen Prozessoren zu fertigen. Mehr über RISC und CISC in einem eigenen Aufsatz.
Ein lange gehegter Traum ist es einen Prozessor zu haben der direkt in einer Hochsprache programmiert werden kann. Vereinzelt gab es auch Projekte wie einen LISP Prozessor der LISP als "Assembler" verstand, doch in der Regel befand man das eine Hochsprache zu komplex wäre und man für zahlreiche "niedrige" Systemaufgaben trotzdem Assembler brauchte.
Schon immer inspirierten sich beide Konzepte: C entstand als hardwarenahe Programmiersprache und beherrscht eigentlich nur die grundlegenden Datentypen des Prozessors. Andererseits wurde der heute in allen Prozessoren anzutreffende Stack wurde zum ersten mal in den sechzigern von Wirth für seine virtuelle Pascal Maschine eingeführt und von den Prozessoren erst später übernommen. Vor allem bei den Datentypen nähern sich die Konzepte an: Anders als ein Prozessor gibt es bei Hochsprachen z.B. verschiedene Datentypen die ein Prozessor nicht kennt :
Zur Unterstützung dessen wurden in vielen Prozessoren Maschinenbefehle eingeführt. So beherrschte bitweise Datenmanipulation z.B. der Z80 mit Befehlen um Bits zu setzen, rückzusetzen oder abzufragen. Da die meisten Programme diese selten benutzten und es oft aus Performancegründen geschickter war Speicher zu verschwenden und ein Byte pro Bit zu verwenden verschwanden diese Befehle allerdings rasch wieder.
Fast alle Prozessoren führten jedoch einen Indexzugriff auf den Speicher ein, meistens mit einem Register als Basisadresse und einem zweiten Register als Offset. Durch erhöhen dieses Offsets in festen Werten konnte man so leicht über ein Array iterieren. Diese Zugriffsmethode ist heute Standard
Auch für Strings gab es eigene Befehle. Die 80x86 Serie kann z.B. mit einigen Befehlen einen String kopieren, eine Subposition eines Zeichen finden oder zwei Strings vergleichen. Bei vielen RISC Prozessoren findet man aber nicht diese Hochspezialisierten Befehle
Funktionsaufrufe in Hochsprachen laufen üblicherweise über den Stack ab: Ein Speicherbereich auf den Register abgelegt werden wobei man immer nur am Ende ein Register sichern (PUSH) oder eines lesen (POP) kann. So werden Argumente an die Funktion auf dem Stack abgelegt und dann von diesem wieder von der Funktion geholt und eventuell Ergebnisse wieder dort abgelegt. Der Stack liegt aber nicht im Prozessor sondern im Hauptspeicher und so ist diese Vorgehensweise sehr zeitaufwendig, weshalb einige Sprachen wie C/C++ mit Makros arbeiten - Codeschnipsel die eingefügt werden und Funktionen ersetzen.
Beim SPARC Prozessor wurde zum ersten Mal das Register Windows eingeführt: Der sehr große Registersatz setzt sich aus immer sichtbaren Registern zusammen und einem Satz von 16 Registern die gewechselt werden können: Beim Aufruf einer Funktion muss nur dieser Kontext gewechselt werden, wobei dies im Prozessor erfolgt und sehr schnell ist. Der Stack ist für Argumente nicht mehr nötig. Diese Technik wurde in Folge auch von anderen Prozessoren übernommen.
Als die 8 Bitter entwickelt wurden war die Welt noch in Ordnung: Man musste sich nicht um die Geschwindigkeit des Speichers kümmern, er war schneller als der Prozessor. Die ersten 8 Bitter arbeiteten mit 1-2.5 MHz und Speicher hatte Zugriffszeiten von 250 ns, ausreichend also für 4 MHz (4 * Taktfrequenz = Speichertakt in ns). Auch als spätere 8 Bitter bis zu 8 MHz erreichten war das kein Problem, inzwischen gab es Speicher mit 120 ns Zugriffszeit. So betrug bei den Prozessoren der achtziger Reihe (8080,8085 und Z80), z.B. die Ausführungszeit eines Befehls mindestens 4 Takten - dies entspricht einem Speicherzyklus.
Doch schon bei den 16 Bittern war's aus mit diesem einfachen Leben, es kamen die ersten Entwicklungen auf die es erlaubten die Taktfrequenz des Prozessors über dem des Speichers zu setzen. Diese Notwendigkeit ergab sich einfach aus den steigenden Taktfrequenzen. 1978 erschien der 8086 mit 5 MHz, die Taktfrequenz stieg zuerst nur langsam an und erreichte bis 1982 8 MHz beim 80286, doch dann begannen die Taktfrequenzen schnell zu steigen: 1985 16 MHz, 1989 33 MHz, 1993 66 MHz... Eine Verdopplung alle 3 Jahre. (Seither noch schneller, von 1998 bis 2001 stiegt die Taktfrequenz von 500 auf 2000 MHz, also um das 4 fache in 3 Jahren).
Doch Speicherchips konnten nicht so schnell gesteigert werden, bis zum Einführung von SD-RAM 1996 stieg innerhalb von 20 Jahren die Zugriffszeit von Speicher von 250 ns auf 60 ns, also um den Faktor 4. Die Taktfrequenz von Prozessoren dagegen von 2.5 MHz auf 200 MHz also um den Faktor 80! Neben dem Konzept des Caches und Prefetches kann man Speicher eigentlich nur durch Banks schneller machen. Dieses Konzept ist nicht neu: Schon bei der Cray 2 hat man einen Rechner mit hoher Taktfrequenz so an Speicher mit langsamer Zykluszeit angeschlossen. Das Konzept ist folgendes: Anstatt einem Speicher nimmt man z.B. 4 Speicherbänke die je ein Viertel des Speichers abdecken. Intelligenterweise verschiebt man diese so, das man folgenden Zugriff hat:
Adresse | Bank | Takt |
4711 | 0 | 1 |
4712 | 1 | 2 |
4713 | 2 | 3 |
4714 | 3 | 4 |
4715 | 0 | 5 |
Wenn man also Code in die CPU holt greift man nacheinander auf alle Bänke zu - in der Regel springt man bei Programmen nicht wild in der Gegend herum sondern arbeitet zumindest einige Befehle linear oder wiederholt in einer Schleife ab. Bei diesem Konzept mit 4 Banks greift also der Prozessor bei jedem 4.ten Takt erst wieder auf denselben Speicher zu. Dieses Konzept wird durch Sprünge, Zugriff auf Daten (die nicht immer ideal liegen müssen) oder Unterprogrammaufrufe allerdings durchkreuzt, in diesem Fall muss der Prozessor warten, wofür sich schon bald den Ausdruck "Wait State" einbürgerte.
Schon beim 8086/88, dem ersten 16 Bitter von Intel wurde daher eine Technik eingeführt um den Prozessor vom Speicher etwas unabhängiger zu machen. Das erste war, das Bus und Ausführungseinheit voneinander getrennt waren. Beim 8086 waren die Befehle daher nicht mehr ein Vielfaches von 4 Takten, denn wenn nur Daten geholt wurden, hatte die Ausführungseinheit bisher nichts zu tun. Nun konnte sie schon an das Dekodieren gehen, während zugehörige Operanden erst aus dem Speicher geholt wurden. Dazu diente auch ein kleiner Zwischenspeicher die Prefetch Queue. Diese Technik wurde in der Folge verbessert. Heute läuft ohne Prefetch nichts mehr. Eine Prefetch Queue ist im einfachsten Fall nur ein kleiner Zwischenspeicher der einfach nur linear Bytes vom Speicher holt. Arbeitet der Prozessor zum Beispiel bei Adresse 4048, so holt der Prozessor einfach schon mal die Bytes 4049,4050,4051... Da Programme normalerweise linear abgearbeitet werden ist das auch sinnvoll. Wenn allerdings der Prozessor Daten braucht die etwas weiter entfernt sind oder eine Verzweigung im Code kommt, dann nützt dieser Zwischenspeicher nichts und es dauert länger ihn wieder neu zu füllen, als wenn man direkt auf den Speicher zugreift. Im Mittel gibt es jedoch einen deutlichen Performancegewinn.
Weiterhin dauerte das Schreiben auf den Speicher länger als das Lesen, außerdem haben Hochsprachenprogramme sehr oft Befehle bei denen die Daten geholt werden, die gerade erst gespeichert wurden. Damit der 8086 nun nicht die veralteten Daten aus der Prefetch-Qeue holte hatte er einen kleinen Schreibpuffer. Die heutigen SD-RAM's haben eine ähnliches Prinzip um die "niedrigen" Zugriffszeiten zu erreichen: Wenn der Rechner Daten haben will, so braucht dies genauso lang wie bei normalen EDO-RAM's, also etwa 55 ns. Danach liefert SD-RAM die nächsten 8 Bytes in schneller Folge nach, da er vorschauend diese schon ausliest - nun in erheblich kürzerer Zeit von 7-10 ns. Braucht der Prozessor diese aber nicht oder wechselt er aus anderen Gründen die Adresse, so ist SD-RAM wieder bei dem langsamen 55 ns Erstzugriff. Das ist auch der Grund warum die Übergänge in den Architekturen (EDO-RAM -> SD RAM -> DDR RAM und das Hochfahren der Taktzeiten für diesen "Burst" Modus so wenig auf die Anwendungen durchschlagen - Im Mittel kommt z.B. von 100 % mehr Daten bei DDR RAM noch 36 % an, wenn man die Zugriffszeit vor dem Burst dazurechnet. Anwendungen werden sogar nur um 10-20 % schneller.
Eine Lösung ist für das Problem derzeit nicht in Sicht. Man kann nur versuchen das Zugriffsverhalten zu optimieren (siehe auch zum Cache) weiter unten. Evt. übernimmt man Anleihen von den Grafikchipsätzen die sehr stark auf schnelles RAM angewiesen sind. Neben sehr schnellem RAM findet man dort auch bis zu 256 Bit breiten Busse und die Chips sind fest eingelötet anstatt über Module mit langen Leitungswegen und kapazitiven Widerständen angebracht. Dadurch werden Bandbreiten bis 20 GB/s erreicht, während im PC Bereich zum Zeitpunkt des Artikels bei 2.7 GB/s Schluss ist. Angesichts sinkender Preise und immer kürzeren Zyklen bei neuen Mainboards, könnte fest eingelöteter Speicher mit besserer Zugriffszeit, zumindest für bestimmte Anwendungsgebiete interessant erscheinen.
Der 80286 verfügte um erheblich längere Prefetch Queues und größere Schreibpuffer. Die Performancesteigerung gelang vor allem aber durch schnellere Befehlsausführung. Mit dem 386 kam nun eine erhebliche Änderung im Aufbau des Prozessors. Er wurde zu einem 32 Bit Prozessor, doch dazu später mehr. Der 386 war aber vor allem der erste Prozessor der erheblich schneller als das RAM war. Damit nun der 386 überhaupt schneller als ein 286 war, musste man dies ausgleichen können und führte das Konzept des Caches ein. Damals war er noch separat auf dem Motherboard untergebracht. Der Prozessor selbst greift zuerst auf den Cache zu. Dieser speichert den Code vom Hauptspeicher zwischen und hat eine Zugriffszeit die den Prozessor nicht ausbremst. Der Cache wird von einem Teil des Prozessors, dem Cachecontroller verwaltet. Er bündelt jeweils 16 oder 32 Bytes zu einer "Cacheline". Wenn der Prozessor neue Daten braucht die nicht im Cache stehen so muss der Cachecontroller diese vom Hauptspeicher laden. Dazu muss er entscheiden welche Cachelines am längsten nicht benutzt wurden. Dazu hat er eine Indextabelle an der jeweils steht welche Cacheline zu welcher Adresse gehört.
Heute ist der Cache nicht mehr direkt gemappt, sondern man speichert für eine Indexadresse mehrere Cachelines ab. Man nennt das mehrfach assoziativ. Ein grundsätzliches Problem ist allerdings das man nun entscheiden muss welche Cacheline verworfen werden muss. Dazu hat ein Prozessor heute eine ausgeklügelte Logik an Bord die den LRU (Least Recently Used) Eintrag herausfindet. Auch beim Schreiben landen die Daten zuerst im Cache, außer die Adresse ist außerhalb des Cache Bereiches. Dann spricht man von Write Miss.
Es stellt sich nun die Frage, wie Cache so schnell sein kann, wenn ich doch oben geschrieben habe, das Speicher im allgemeinen eher langsam ist. Nun die Antwort ist simpel, es wird eine andere Technologie verwendet. "Normaler" Speicher oder DRAM besteht aus einem Kondensator und einem Transistor der es erlaubt die Ladung dieses Kondensators auf den Datenbus zu legen. Das Entladen und Laden dauert aber jedem Kondensator eine kurze Zeitspanne. Cache Speicher (SRAM) bestehen aus 6 (L2) bzw. 8 (L1) Transistoren, die eine Ladung in einem Flip-Flop speichern. Diese können wesentlich schneller ausgelesen und beschrieben werden als DRAM. Dafür ist der Flächenverbrauch für ein Bit viermal so groß.
Mit dem 486 wurde der Cache in den Prozessor integriert und L1 Cache genannt, ein zweiter Cache auf dem Motherboard wurde zum L2 Cache. Das Prinzip: Sind die Daten nicht im kleinen aber schnellen L1 Cache, so sind sie vielleicht im größeren und etwas langsameren L2 Cache, und erst dann muss man auf den Speicher zugreifen. Bei einem Designs wie dem K6-III oder Alpha gibt es sogar L3 Caches mit bis zu 2 MByte Größe. Auf den Prozessorbildern der unten stehenden Bilder der Pentium III / 4 und Prozessoren fallen die Caches durch die Felder mit gleichmäßiger hoher Reflexion auf. Während heutzutage alle Prozessoren Caches haben (Bei Motorola z.B. mit der MC 68030 eingeführt), sind diese bei Mikrocontrollern eher selten. Grund dafür ist der Preis - Die Caches in SRAM Technologie haben erheblich mehr Transistoren als die Prozessorkerne, so das die Ausbeute pro Waver sinkt. Darüber hinaus benötigen die CPUs so mehr Strom.
Die Verwaltung von Caches, sowie ihr schneller Zugriff ist heute essentiell für die Performance eines Prozessors. Ein Großteil der Entwicklung und Chipfläche geht heute auf die Optimierung der Caches drauf. Was der Cache ausmacht kann man leicht selbst ausprobieren. Man kann im BIOS nämlich diese ausschalten. Macht man das so ist auch der neueste Pentium 4 nicht mehr viel schneller als ein langsamer 486 er (dieser aber mit Cache).
Eine Untersuchung von ct beim Pentium 4 mit 3.06 GHz ergab, dass dieser bei den meisten Programmen intern nur auf den Speicher wartete. Im Worst Case (Daten weder im L1 noch L2 Cache und auch nicht über Burst einlesbar) dauerte es 124 ns bis die Daten heran tuckern - In dieser Zeit hätte der Prozessor 380 Instruktionen ausführen können. Der Grund liegt daran, das heutige Architekturen wie SD-RAM, DDR-RAM oder RAMBUS zwar sehr schnell die Daten übertragen wenn sie anliegen, die Zugriffsgeschwindigkeit aber sehr schlecht ist. Abhilfe könnten hier entweder RAM's mit höherer Zugriffsgeschwindigkeit bieten, wie sie z.B. für Grafikkarten eingesetzt werden, vielleicht als zusätzlicher L3 oder L4 Cache. Billiger geht es indem man mehrere Bänke bestückt und die Zugriffe auf diese verteilt. Dies wird bei größeren Servern so gemacht. Ein PC kommt aus Preisgründen meistens nur mit einem RAM Riegel und hat meistens auch nur 2-3 Steckplätze für Speicher.
Mit dem 386 zog auch eine neue Möglichkeit in den Prozessor ein: Virtueller Speicher. Obgleich der 386 4 Gigabyte Speicher adressieren konnte spendierte ihm Intel eine MMU. Diese Memory Management Unit tat nun nichts anderes als für verschiedene Programme ihnen jeweils vorzuspielen, sie hätten den gesamten 4 Gigabyte Adressraum für sich alleine. Dabei nutzt sie dafür eine 48 Bit Adresse. Die MMU mappte diese dann im realen Speicher oder wenn dieser nicht reichte informierte der Prozessor das Betriebsystem, das er nun den Speicher von Programm × gerade braucht und dieses doch den Inhalt mal auf die Festplatte auslagern sollte. Damit dies nicht in zuviel Arbeit ausartet wurde der Speicher in kleine Scheiben von 4 Kilobyte oder 4 Megabyte aufgeteilt und jeweils die Belegung von einer 4 Kilobyte Scheibe in einer Tabelle, der TLB gespeichert.
Mit dem Pentium-Pro (P6) und den auf seiner Architektur aufbauenden Modellen Pentium II und III wurde dieses Speichermanagement verbessert indem man nun nicht nur 4 MByte und 4 KByte große Seiten ansprechen konnte sondern jede Zweierpotenz, die TLB wurde erweitert und der normale Adressraum von 4 auf 64 Gigabyte erhöht. Alle Windows Varianten bleiben aber noch immer im 4 Gigabyte Rahmen. Es macht also keinen Sinn einen Windows 2000 Server mit mehr als 4 GByte Speicher auszustatten. (Zumal das Betriebssystem die oberen 2 GByte des Speichers für sich reserviert).
Virtueller Speicher ist nichts neues, schon die berühmte VAX hatte ihren Namen 1977 nach diesem Konzept bekommen. Beim Intel 386 wird vom Prozessor z.B. ein 48 Bit breiter virtueller Speicher unterstützt. In diesem kann es sehr viele Prozesse mit max. 32 Bit Breite (4 GB) geben. Die Auslagerung übernehmen Betriebssystemroutinen die vom Prozessor über bestimmte Interrupts angestoßen werden, wir haben es hier also mit einer Hard/Softwarelösung zu tun - entsprechend hat jedes Betriebssystem eine etwas andere Lösung das Swappen zu realisieren. Allen gemeinsam ist das man auf der Festplatte einen Bereich (Datei, Partion) hat die ausgelagerte Prozesse aufnimmt. Damit kann man:
Intel bekam mit der Weiterentwicklung des 8086 immer mehr Probleme. Anders als Intel dachte, wurden zum Beispiel die neuen Modi und die 32 Bit Register des 286 und 386 nicht in Betriebssystemen genutzt, so das bis Anfang der neunziger Jahre ein 386 oder 486 im wesentlichen als schnelle 8086 genutzt wurden. Natürlich stieg die Performance zuerst rasch an, durch die Reduktion der durchschnittlichen Ausführungsgeschwindigkeit der Befehle an. Ein 80286 war 2,4 mal schneller als 8086, doch dann wurde der Zuwachs immer geringer ein 386 noch 1,6 mal schneller als ein 286, ein 486 noch 1,5 mal schneller als der 386 und der Pentium nur noch 1,3 mal schneller. (Alle Angaben bei gleicher Taktfrequenz). Damit man ab dem 486 überhaupt noch einen Gewinn erzielen konnte ging man zu einem neuen Konzept über. Man führte beim Pentium zwei Fliesskomma und zwei Integer Einheiten ein. Idealerweise hätte man so die Performance so um das doppelte steigern können. Man nennt ein solches Design superskalar. Das Prinzip: Der Prozessor hat mehrere Funktionseinheiten die entweder durch eine gemeinsame Pipeline oder separate Pipelines aus dem L1 Cache versorgt werden. Es gibt mindestens 4 grundlegend verschiedene Funktionseinheiten:
MOV AX,100 ; 100 in Register AX laden
MOV BX,200 ; 200 in Register BX laden
ADD AX,BX ; AX und BX addieren, das Ergebnis steht in AX
MOV [4567],AX; Das Ergebnis in Adresse 4567 speichern
INC CX ; CX um 1 erhöhen
DEC DX ; DX um 1 erniedrigen