Warum ist x86 hässlich? Warum wird es im Vergleich zu anderen als minderwertig angesehen? [geschlossen]


105

Kürzlich habe ich einige SO-Archive gelesen und bin auf Aussagen gegen die x86-Architektur gestoßen.

und viele weitere Kommentare wie

Ich habe versucht zu suchen, aber keine Gründe gefunden. Ich finde x86 wahrscheinlich nicht schlecht, weil dies die einzige Architektur ist, mit der ich vertraut bin.

Kann mir jemand freundlicherweise Gründe geben, x86 im Vergleich zu anderen als hässlich / schlecht / minderwertig zu betrachten?


1
Ich gehe mit S & A auf der Grundlage der bisherigen Antworten, aber ich werde nebenbei bemerken, dass CISC kein Problem für den m68k-Befehlssatz ist. x86 ist was es ist und Sie können es behalten.
dmckee --- Ex-Moderator Kätzchen

Was ist "S & A"? "CISC ist kein Problem für den m68k-Befehlssatz." -- Warum nicht?
Krallen

5
Die Chips der motorala 68000-Serie haben eine hohe CISC-Architektur, aber einen einheitlichen, ziemlich orthogonalen und sehr einfachen Befehlssatz. Warum der Unterschied zu x86? Ich weiß es nicht. Beachten Sie jedoch, dass es einen großen Unterschied zwischen der Komplexität des Chips und der Komplexität des Befehlssatzes gibt (dh der Schnittstelle, die ein Assembly-Programmierer sieht).
dmckee --- Ex-Moderator Kätzchen

4
+1 für eine sehr interessante Frage.
Turing Complete

1
Aktuelle Studie zur Energieeffizienz verschiedener Prozessoren mit einer guten Diskussion darüber, was CISC- und RISC-Designs vorangetrieben hat. extremetech.com/extreme/…

Antworten:


93

Einige mögliche Gründe dafür:

  1. x86 ist eine relativ alte ISA (ihre Vorfahren waren immerhin 8086)
  2. x86 hat sich mehrmals erheblich weiterentwickelt, aber Hardware ist erforderlich, um die Abwärtskompatibilität mit alten Binärdateien aufrechtzuerhalten. Beispielsweise unterstützt moderne x86-Hardware weiterhin die native Ausführung von 16-Bit-Code. Darüber hinaus gibt es mehrere Speicheradressierungsmodelle, mit denen älterer Code auf demselben Prozessor zusammenarbeiten kann, z. B. der Real-Modus, der geschützte Modus, der virtuelle 8086-Modus und der (amd64) Long-Modus. Dies kann für manche verwirrend sein.
  3. x86 ist eine CISC-Maschine. Für eine lange Zeit bedeutete dies, dass es langsamer war als RISC-Maschinen wie MIPS oder ARM, da Befehle Datenabhängigkeit und Flags aufweisen, was die Implementierung der meisten Formen der Parallelität auf Befehlsebene schwierig macht. Moderne Implementierungen übersetzen die x86-Anweisungen in RISC-ähnliche Anweisungen, die unter dem Deckmantel als " Micro-Ops " bezeichnet werden, um die Implementierung dieser Art von Optimierungen in Hardware praktisch zu gestalten.
  4. In mancher Hinsicht ist das x86 nicht minderwertig, es ist nur anders. Beispielsweise wird die Eingabe / Ausgabe auf den meisten Architekturen als Speicherzuordnung behandelt, nicht jedoch auf dem x86. (Hinweis: Moderne x86-Computer bieten normalerweise DMA- Unterstützung und kommunizieren über Speicherzuordnung mit anderer Hardware. Die ISA verfügt jedoch weiterhin über E / A-Anweisungen wie INund OUT)
  5. Der x86 ISA verfügt über sehr wenige Architekturregister, die Programme dazu zwingen können, häufiger als sonst erforderlich durch den Speicher zu wechseln. Die zusätzlichen Anweisungen, die dazu erforderlich sind, erfordern Ausführungsressourcen, die für nützliche Arbeit aufgewendet werden können, obwohl eine effiziente Weiterleitung des Geschäfts erforderlich isthält die Latenz niedrig. Moderne Implementierungen mit Registerumbenennung in eine große physische Registerdatei können viele Anweisungen im Flug halten, aber das Fehlen von Architekturregistern war für 32-Bit x86 immer noch eine erhebliche Schwäche. Die Erhöhung von x86-64 von 8 auf 16 Ganzzahl- und Vektorregister ist einer der größten Faktoren im 64-Bit-Code, der schneller als 32-Bit ist (zusammen mit dem effizienteren Registeraufruf-ABI), nicht die vergrößerte Breite jedes Registers. Eine weitere Erhöhung von 16 auf 32 Ganzzahlregister würde einigen helfen, aber nicht so viel. (AVX512 erhöht sich jedoch auf 32 Vektorregister, da Gleitkomma-Code eine höhere Latenz aufweist und häufig mehr Konstanten benötigt.) ( Siehe Kommentar )
  6. Der x86-Assemblycode ist kompliziert, da x86 eine komplizierte Architektur mit vielen Funktionen ist. Eine Anweisungsliste für ein typisches MIPS-Gerät passt auf ein einzelnes Stück Papier im Letter-Format. Die entsprechende Auflistung für x86 füllt mehrere Seiten, und die Anweisungen leisten nur mehr, sodass Sie häufig eine ausführlichere Erklärung ihrer Funktionsweise benötigen, als eine Auflistung bieten kann. Zum Beispiel benötigt der MOVSBBefehl einen relativ großen Block C-Code, um zu beschreiben, was er tut:

    if (DF==0) 
      *(byte*)DI++ = *(byte*)SI++; 
    else 
      *(byte*)DI-- = *(byte*)SI--;
    

    Dies ist eine einzelne Anweisung, die ein Laden, ein Speichern und zwei Additionen oder Subtraktionen (gesteuert durch eine Flag-Eingabe) ausführt, von denen jede separate Anweisungen auf einer RISC-Maschine wäre.

    Während die Einfachheit von MIPS (und ähnlichen Architekturen) sie nicht unbedingt überlegen macht, ist es für das Unterrichten einer Einführung in die Assembler-Klasse sinnvoll, mit einer einfacheren ISA zu beginnen . Einige Assembly-Klassen unterrichten eine ultra-vereinfachte Teilmenge von x86 namens y86 , die über den Punkt hinaus vereinfacht wird, dass sie für den tatsächlichen Gebrauch nicht nützlich ist (z. B. keine Schichtanweisungen), oder einige unterrichten nur die grundlegenden x86-Anweisungen.

  7. Der x86 verwendet Opcodes variabler Länge, die die Hardwarekomplexität in Bezug auf das Parsen von Anweisungen erhöhen. In der modernen Zeit werden diese Kosten immer geringer, da CPUs durch die Speicherbandbreite immer mehr eingeschränkt werden als durch Rohberechnungen. Viele Artikel und Einstellungen zum "x86-Bashing" stammen jedoch aus einer Zeit, in der diese Kosten vergleichsweise viel höher waren.
    Update 2016: Anandtech hat eine Diskussion zu Opcode-Größen unter x64 und AArch64 veröffentlicht .

EDIT: Dies soll keine Bash der x86 sein! Party. Ich hatte keine andere Wahl, als ein bisschen zu schlagen, wenn man bedenkt, wie die Frage formuliert ist. Mit Ausnahme von (1) wurden all diese Dinge aus guten Gründen getan (siehe Kommentare). Intel-Designer sind nicht dumm - sie wollten mit ihrer Architektur einige Dinge erreichen, und dies sind einige der Steuern, die sie zahlen mussten, um diese Dinge Wirklichkeit werden zu lassen.


17
Es ist ein Kompromiss. Es ist eine Stärke, dass die Binärgröße möglicherweise kleiner ist, aber es ist eine Schwäche, dass Sie sehr komplizierte Hardware benötigen, um einen Parser für diese Anweisungen zu implementieren. Die überwiegende Mehrheit der Anweisungen hat ohnehin die gleiche Größe - der Hauptgrund für Opcodes mit variabler Länge auf x86 liegt darin, dass sie beschlossen, Funktionen hinzuzufügen, und feststellten, dass sie in der Anzahl der Bits, mit denen sie arbeiten mussten, nicht das darstellen konnten, was sie wollten . Die überwiegende Mehrheit der Menschen befasst sich nicht so sehr mit der Binärgröße wie mit der Hardwarekomplexität oder dem Stromverbrauch.
Billy ONeal

8
@Joey Adams: Vergleichen Sie die Anweisungen des x86 mit variabler Länge mit dem Daumenmodus des ARM ( en.wikipedia.org/wiki/ARM_architecture#Thumb ). Der Daumenmodus führt zu einem erheblich kleineren Objektcode für den ARM, da die kürzeren Anweisungen direkt normalen Anweisungen zugeordnet sind. Da es jedoch eine 1: 1-Zuordnung zwischen den größeren und den kleineren Befehlen gibt, ist die Parsing-Hardware einfach zu implementieren. Die Anweisungen mit variabler Länge des x86 bieten diese Vorteile nicht, da sie überhaupt nicht so konzipiert wurden.
Billy ONeal

7
(6) Nicht jeder Op-Code muss von jedem Programm verwendet werden, aber verdammt, wenn ich SSE3 brauche, bin ich froh, dass ich es habe.
Chris K

4
@ Chris Kaminski: Wie wirkt sich das nicht auf die Hardware aus? Sicher, auf einem modernen Computer in voller Größe wird es niemanden interessieren, aber wenn ich so etwas wie ein Handy mache, ist mir der Stromverbrauch wichtiger als fast alles andere. Die Opcodes mit variabler Länge verlängern die Ausführungszeit nicht, aber die Decodierhardware benötigt zum Betrieb noch Strom.
Billy ONeal

5
Dies ist eines der Dinge, die den x86-Befehlssatz so hässlich machen, da er nicht entscheiden kann, ob es sich um einen Akkumulator oder eine auf Registerdateien basierende Architektur handelt (obwohl dies größtenteils mit dem 386 behoben wurde, wodurch der Befehlssatz viel orthogonaler wurde , unabhängig davon, was die 68k-Fans dir sagen).
Ninjalj

25

Der Hauptgrund für x86 ist meiner Meinung nach der CISC-Ursprung - der Befehlssatz enthält viele implizite Abhängigkeiten. Diese Interdependenzen machen es schwierig, Dinge wie die Neuordnung von Befehlen auf dem Chip durchzuführen, da die Artefakte und die Semantik dieser Interdependenzen für jeden Befehl erhalten bleiben müssen.

Beispielsweise ändern die meisten Anweisungen zum Hinzufügen und Entfernen von x86-Ganzzahlen das Flags-Register. Nach dem Hinzufügen oder Subtrahieren besteht die nächste Operation häufig darin, das Flags-Register auf Überlauf, Vorzeichenbit usw. zu überprüfen. Wenn danach ein weiteres Add erfolgt, ist es sehr schwierig zu sagen, ob es sicher ist, mit der Ausführung des zweiten Adds zu beginnen bevor das Ergebnis der 1. Addition bekannt ist.

In einer RISC-Architektur würde der Befehl add die Eingabeoperanden und die Ausgaberegister angeben, und alles über die Operation würde nur unter Verwendung dieser Register stattfinden. Dies macht es viel einfacher, Add-Operationen zu entkoppeln, die nahe beieinander liegen, da es kein Register für blühende Flags gibt, das alles zwingt, eine einzelne Datei auszurichten und auszuführen.

Der DEC Alpha AXP-Chip, ein RISC-Design im MIPS-Stil, war in den verfügbaren Anweisungen schmerzlich spartanisch, aber der Befehlssatz wurde entwickelt, um implizite Registerabhängigkeiten zwischen Befehlen zu vermeiden. Es gab kein hardwaredefiniertes Stapelregister. Es gab kein Hardware-definiertes Flags-Register. Sogar der Anweisungszeiger war vom Betriebssystem definiert. Wenn Sie zum Anrufer zurückkehren möchten, müssen Sie herausfinden, wie der Anrufer Sie wissen lässt, an welche Adresse Sie zurückkehren sollen. Dies wurde normalerweise durch die OS-Aufrufkonvention definiert. Auf dem x86 wird es jedoch durch die Chip-Hardware definiert.

Über 3 oder 4 Generationen von Alpha-AXP-Chip-Designs hinweg ging die Hardware von einer wörtlichen Implementierung des spartanischen Befehlssatzes mit 32 Int-Registern und 32 Float-Registern zu einer massiv außer Betrieb befindlichen Ausführungs-Engine mit 80 internen Registern über. Ergebnisweiterleitung (wobei das Ergebnis einer vorherigen Anweisung an eine spätere Anweisung weitergeleitet wird, die vom Wert abhängt) und alle Arten von wilden und verrückten Leistungssteigerern. Und mit all diesen Schnickschnack war der AXP-Chip-Chip immer noch erheblich kleiner als der vergleichbare Pentium-Chip-Chip dieser Zeit, und der AXP war verdammt viel schneller.

Diese Art von Leistungsschüben, die die Leistung steigern, werden im x86-Stammbaum nicht angezeigt, da die Komplexität des x86-Befehlssatzes viele Arten von Ausführungsoptimierungen unerschwinglich teuer, wenn nicht unmöglich macht. Intels Geniestreich bestand darin, die Implementierung des x86-Befehlssatzes in Hardware nicht mehr aufzugeben - alle modernen x86-Chips sind tatsächlich RISC-Kerne, die die x86-Befehle bis zu einem gewissen Grad interpretieren und in internen Mikrocode übersetzen, der die gesamte Semantik des ursprünglichen x86 beibehält Anweisung, ermöglicht aber ein wenig von diesem RISC außer Betrieb und andere Optimierungen über den Mikrocode.

Ich habe viel x86-Assembler geschrieben und kann die Bequemlichkeit seiner CISC-Wurzeln voll und ganz schätzen. Aber ich wusste nicht genau, wie kompliziert x86 war, bis ich einige Zeit damit verbracht habe, Alpha AXP Assembler zu schreiben. Ich war begeistert von der Einfachheit und Einheitlichkeit von AXP. Die Unterschiede sind enorm und tiefgreifend.


6
Ich werde mir kein CISC-Bashing per se anhören, es sei denn und bis Sie m68k erklären können.
dmckee --- Ex-Moderator Kätzchen

2
Ich bin mit dem m68k nicht vertraut, daher kann ich es nicht kritisieren.
Dthorpe

4
Ich denke nicht, dass diese Antwort schlecht genug ist, um abzustimmen, aber ich denke, dass das gesamte Argument "RISC ist kleiner und schneller als CISC" in der modernen Zeit nicht wirklich relevant ist. Sicher, der AXP war für seine Zeit verdammt viel schneller, aber Tatsache ist, dass moderne RISCs und moderne CISCs in Bezug auf die Leistung ungefähr gleich sind. Wie ich in meiner Antwort sagte, ist die leichte Leistungsstrafe für die x86-Dekodierung ein Grund, x86 nicht für so etwas wie ein Mobiltelefon zu verwenden, aber das ist kein Argument für einen Desktop oder ein Notebook in voller Größe.
Billy ONeal

4
@ Billy: Größe ist mehr als nur Codegröße oder Anweisungsgröße. Intel zahlt eine ziemliche Strafe für die Chipoberfläche, um die Hardwarelogik für all diese speziellen Anweisungen zu implementieren, ob RISC-Mikrocode-Kern unter der Haube oder nicht. Die Größe der Matrize wirkt sich direkt auf die Herstellungskosten aus, daher ist dies bei modernen Systemdesigns immer noch ein berechtigtes Anliegen.
Dthorpe

1
@dthorpe: Ich bin mit den meisten, wenn nicht allen, die Sie geschrieben haben, nicht einverstanden. Seit dem 8086 mussten Sie sich keine Sorgen mehr machen, ob es sicher war, eine addnach der anderen auszuführen add. Die Regeln sind klar. Sie müssen sich auch nicht mit der Neuordnung von Anweisungen befassen. Seit dem Pentium Pro Mitte der 90er Jahre erledigt die CPU das für Sie. Was Sie erwähnen, mag vor 20 Jahren ein Problem gewesen sein, aber ich sehe keinen Grund, es heutzutage gegen die x86-Architektur zu halten.
Nathan Fellman

21

Die x86-Architektur stammt aus dem Design des 8008-Mikroprozessors und seiner Verwandten. Diese CPUs wurden in einer Zeit entwickelt, in der der Speicher langsam war und wenn Sie dies auf dem CPU-Chip tun konnten, war es oft viel schneller. Der CPU-Chipraum war jedoch auch teuer. Diese beiden Gründe sind, warum es nur eine kleine Anzahl von Registern gibt, die dazu neigen, spezielle Zwecke zu erfüllen, und einen komplizierten Befehlssatz mit allen möglichen Fallstricken und Einschränkungen.

Andere Prozessoren aus derselben Zeit (z. B. die 6502-Familie) weisen ähnliche Einschränkungen und Besonderheiten auf. Interessanterweise waren sowohl die 8008-Serie als auch die 6502-Serie als eingebettete Controller gedacht. Schon damals wurde erwartet, dass eingebettete Controller im Assembler programmiert werden und in vielerlei Hinsicht eher dem Assembler-Programmierer als dem Compiler-Writer gerecht werden. (Sehen Sie sich den VAX-Chip an, um zu sehen, was passiert, wenn Sie sich um das Schreiben des Compilers kümmern.) Die Designer haben nicht erwartet, dass sie zu Allzweck-Computerplattformen werden. Dafür waren Dinge wie die Vorgänger der POWER-Architektur da. Die Heimcomputer-Revolution hat das natürlich geändert.


4
+1 für die einzige Antwort von jemandem, der tatsächlich einen historischen Hintergrund zu diesem Thema zu haben scheint.
Billy ONeal

3
Das Gedächtnis war immer langsam. Es ist heute möglicherweise (relativ gesehen) langsamer als damals, als ich 1982 mit Z80 und CP / M begann. Das Aussterben ist nicht der einzige Weg der Evolution, da mit dem Aussterben diese bestimmte Evolutionsrichtung aufhört. Ich würde sagen, das x86 hat sich in seinem 28-jährigen Bestehen (bisheriges Bestehen) gut angepasst.
Olof Forshell

4
Die Speichergeschwindigkeiten erreichten um die Zeit des 8086 kurzzeitig nahezu die Parität mit den CPUs. Der 9900 von Texas Instruments verfügt über ein Design, das nur funktioniert, weil dies passiert ist. Aber dann raste die CPU wieder voran und ist dort geblieben. Erst jetzt gibt es Caches, um dies zu verwalten.
Statik

3
@Olof Forshell: Es war Assembler-kompatibel, da 8080-Assembler-Code in 8086-Code übersetzt werden konnte. Unter diesem Gesichtspunkt waren es 8080 plus Erweiterungen, ähnlich wie Sie 8080 als 8008 plus Erweiterungen anzeigen konnten.
David Thornley

3
@Olof Forshell: Nur dass der 8086 dafür entwickelt wurde. Es war eine Erweiterung des 8080, und die meisten (möglicherweise alle) 8080-Anweisungen wurden eins zu eins mit offensichtlich ähnlicher Semantik zugeordnet. Dies gilt nicht für die IBM 360-Architektur, unabhängig davon, auf welche Weise Sie sie pushen möchten.
David Thornley

13

Ich habe hier einige zusätzliche Aspekte:

Betrachten Sie die Operation "a = b / c" x86 würde dies als implementieren

  mov eax,b
  xor edx,edx
  div dword ptr c
  mov a,eax

Als zusätzlichen Bonus der Div-Anweisung enthält edx den Rest.

Ein RISC-Prozessor würde erfordern, zuerst die Adressen von b und c zu laden, b und c aus dem Speicher in Register zu laden, die Division durchzuführen und die Adresse von a zu laden und dann das Ergebnis zu speichern. Dst, src-Syntax:

  mov r5,addr b
  mov r5,[r5]
  mov r6,addr c
  mov r6,[r6]
  div r7,r5,r6
  mov r5,addr a
  mov [r5],r7

Hier wird es normalerweise keinen Rest geben.

Wenn Variablen über Zeiger geladen werden sollen, können beide Sequenzen länger werden, obwohl dies für das RISC weniger wahrscheinlich ist, da möglicherweise ein oder mehrere Zeiger bereits in ein anderes Register geladen sind. x86 hat weniger Register, sodass die Wahrscheinlichkeit, dass sich der Zeiger in einem von ihnen befindet, geringer ist.

Vor-und Nachteile:

Die RISC-Befehle können mit umgebendem Code gemischt werden, um die Befehlsplanung zu verbessern. Dies ist bei x86 weniger möglich, da dies stattdessen (je nach Reihenfolge mehr oder weniger gut) in der CPU selbst funktioniert. Die obige RISC-Sequenz ist in einer 32-Bit-Architektur typischerweise 28 Byte lang (7 Befehle mit jeweils 32 Bit / 4 Byte Breite). Dies führt dazu, dass der Off-Chip-Speicher beim Abrufen der Anweisungen (sieben Abrufe) mehr funktioniert. Die dichtere x86-Sequenz enthält weniger Anweisungen, und obwohl ihre Breite variiert, sehen Sie dort wahrscheinlich auch durchschnittlich 4 Bytes / Anweisung. Selbst wenn Sie Anweisungs-Caches haben, um dies zu beschleunigen, bedeutet sieben Abrufe, dass Sie im Vergleich zum x86 an anderer Stelle ein Defizit von drei haben, das Sie ausgleichen müssen.

Die x86-Architektur mit weniger Registern zum Speichern / Wiederherstellen bedeutet, dass wahrscheinlich Thread-Switches durchgeführt und Interrupts schneller als bei RISC verarbeitet werden. Mehr Register zum Speichern und Wiederherstellen erfordern mehr temporären RAM-Stapelspeicher für Interrupts und mehr permanenten Stapelspeicher zum Speichern von Thread-Zuständen. Diese Aspekte sollten x86 zu einem besseren Kandidaten für die Ausführung von reinem RTOS machen.

Persönlicher finde ich es schwieriger, RISC-Assemblys als x86 zu schreiben. Ich löse dieses Problem, indem ich die RISC-Routine in C schreibe, den generierten Code kompiliere und ändere. Dies ist vom Standpunkt der Codeproduktion effizienter und vom Standpunkt der Ausführung wahrscheinlich weniger effizient. Alle diese 32 Register, um den Überblick zu behalten. Bei x86 ist es umgekehrt: 6-8 Register mit "echten" Namen machen das Problem leichter handhabbar und geben mehr Vertrauen, dass der erzeugte Code wie erwartet funktioniert.

Hässlich? Das liegt im Auge des Betrachters. Ich bevorzuge "anders".


a, b und c in meinen Beispielen sollten als speicherbasierte Variablen und nicht als unmittelbare Werte angesehen werden.
Olof Forshell

... "dword ptr" wird verwendet, um die Größe einer Variablen anzugeben, deren Größe nicht bekannt ist, wenn sie beispielsweise einfach als extern deklariert wird oder wenn Sie faul waren.
Olof Forshell

2
Das ist nicht das erste Mal, dass ich den Vorschlag hörte, es zuerst in C zu schreiben und dann in Assembler zu destillieren. Das hilft definitiv
Joe Plante

In den frühen Tagen waren alle Prozessoren RISC. CISC wurde als Schadensbegrenzungsstrategie für Eisen-Core-Speichersysteme entwickelt, die SEHR langsam waren. Daher belastete CISC mit weniger, leistungsfähigeren Anweisungen das Speichersubsystem weniger und nutzte die Bandbreite besser aus. Ebenso wurden Register ursprünglich als On-Chip-Speicherplätze in der CPU für Akkumulationen angesehen. Das letzte Mal, dass ich eine RISC-Maschine ernsthaft verglichen habe, war 1993 - SPARC und HP Prisim. SPARC war auf der ganzen Linie schrecklich. Prisim war bis zu 20x so schnell wie ein 486 bei add / sub / mul, saugte aber an Transzendentalen. CISC ist besser.

@OlofForshell Sie sagen, there typically won't be a reminderaber Wiki sagt, dass Mips es haben: en.wikipedia.org/wiki/MIPS_instruction_set#Integer
Alex Zhukovskiy

10

Ich denke, diese Frage hat eine falsche Annahme. Es sind hauptsächlich RISC-besessene Akademiker, die x86 als hässlich bezeichnen. In der Realität kann der x86-ISA einzelne Befehlsoperationen ausführen, für die 5-6 Befehle für RISC-ISAs erforderlich sind. RISC-Fans könnten dem entgegenwirken, dass moderne x86-CPUs diese "komplexen" Anweisungen in Mikroops zerlegen. jedoch:

  1. In vielen Fällen ist das nur teilweise oder gar nicht wahr. Die nützlichsten "komplexen" Anweisungen in x86 sind beispielsweise mov %eax, 0x1c(%esp,%edi,4)Adressierungsmodi, die nicht aufgeschlüsselt sind.
  2. Was auf modernen Maschinen oft wichtiger ist, ist nicht die Anzahl der Zyklen (da die meisten Aufgaben nicht an die CPU gebunden sind), sondern die Auswirkung des Codes auf den Anweisungscache. 5-6 Befehle mit fester Größe (normalerweise 32 Bit) wirken sich auf den Cache viel mehr als auf einen komplexen Befehl aus, der selten mehr als 5 Byte umfasst.

x86 hat vor etwa 10 bis 15 Jahren wirklich alle guten Aspekte von RISC aufgegriffen, und die verbleibenden Eigenschaften von RISC (eigentlich die definierende - der minimale Befehlssatz) sind schädlich und unerwünscht.

Neben den Kosten und der Komplexität der Herstellung von CPUs und deren Energiebedarf ist x86 die beste ISA . Jeder, der Ihnen etwas anderes sagt, lässt Ideologie oder Agenda ihrer Argumentation im Wege stehen.

Wenn Sie dagegen auf eingebettete Geräte abzielen, bei denen die Kosten der CPU zählen, oder auf eingebettete / mobile Geräte, bei denen der Energieverbrauch im Vordergrund steht, sind ARM oder MIPS wahrscheinlich sinnvoller. Denken Sie daran, dass Sie sich immer noch mit dem zusätzlichen RAM und der zusätzlichen Binärgröße befassen müssen, die für die Verarbeitung von Code erforderlich sind, der leicht 3-4-mal größer ist, und Sie werden nicht in der Lage sein, sich der Leistung zu nähern. Ob dies wichtig ist, hängt stark davon ab, was Sie darauf ausführen.


3
Wo der Energieverbrauch im Vordergrund steht, ist ARM oder MIPS wahrscheinlich sinnvoller . Wenn es also mindestens einen Aspekt gibt, bei dem ARM oder MIPS sinnvoller sind, ist x86 dann nicht unbedingt die beste ISA?
Shahbaz

Deshalb habe ich "die Besten" mit "abgesehen von den Kosten ... und ihrem Energiebedarf" qualifiziert.
R .. GitHub STOP HELPING ICE

1
Ich denke, Intel hat die CPU-Geschwindigkeit gedrosselt, und kleinere Chipgrößen haben das Leistungsdifferential weitgehend beseitigt. Die neue Celeron Dual 64-Bit-CPU mit 64k L1- und 1MB L2-Caches ist ein 7,5-Watt-Chip. Es ist meine "Starbucks" Hangout-Maschine, und die Akkulaufzeit ist lächerlich lang und lässt Ringe um eine P6-Maschine laufen. Als Typ, der hauptsächlich Gleitkommaberechnungen durchführt, habe ich RISC vor langer Zeit aufgegeben. Es kriecht nur. Insbesondere SPARC war grausam eisig. Das perfekte Beispiel dafür, warum RISC scheiße ist, war die Intel i860-CPU. Intel war nie wieder dort.

@RocketRoy: 7,5 Watt sind für ein Gerät, das rund um die Uhr mit Strom versorgt wird (und nicht die ganze Zeit nützliche Berechnungen durchführt) oder mit einem 3,7 V / 2000 mAh-Akku betrieben wird, nicht wirklich akzeptabel.
R .. GitHub STOP HELPING ICE

2
@RocketRoy "Intel i860 CPU. Intel ist nie wieder dorthin gegangen." Nach ein wenig Recherche klingt der i860 sehr nach Itanium: VLIW, vom Compiler geordnete Anweisungsparallelität ....
Jonathon Reinhart

9

Die x86-Assembler-Sprache ist nicht so schlecht. Wenn Sie zum Maschinencode gelangen, wird es wirklich hässlich. Befehlskodierungen, Adressierungsmodi usw. sind viel komplizierter als bei den meisten RISC-CPUs. Und aus Gründen der Abwärtskompatibilität ist zusätzlicher Spaß eingebaut - Dinge, die nur dann eingesetzt werden, wenn sich der Prozessor in einem bestimmten Zustand befindet.

In 16-Bit-Modi kann die Adressierung beispielsweise geradezu bizarr erscheinen. Es gibt einen Adressierungsmodus für [BX+SI], aber keinen für [AX+BX]. Solche Dinge erschweren in der Regel die Registernutzung, da Sie sicherstellen müssen, dass sich Ihr Wert in einem Register befindet, das Sie nach Bedarf verwenden können.

(Glücklicherweise ist der 32-Bit-Modus viel vernünftiger (obwohl er selbst manchmal noch etwas seltsam ist - zum Beispiel Segmentierung), und 16-Bit-x86-Code ist außerhalb von Bootloadern und einigen eingebetteten Umgebungen weitgehend irrelevant.)

Es gibt auch die Reste aus alten Zeiten, als Intel versuchte, x86 zum ultimativen Prozessor zu machen. Anweisungen, die ein paar Bytes lang sind und Aufgaben ausführen, die eigentlich niemand mehr erledigt, weil sie offen gesagt zu langsam oder zu kompliziert waren. Die Anweisungen ENTER und LOOP für zwei Beispiele: Beachten Sie, dass der C-Stack-Frame-Code für die meisten Compiler wie "push ebp; mov ebp, esp" und nicht "enter" lautet.


2
Ich glaube, das Problem "Enter" versus "Push / Mov" ist aufgetreten, weil auf einigen Prozessoren "Push / Mov" schneller ist. Auf einigen Prozessoren ist "Enter" schneller. So ist das Leben.
Dietrich Epp

4
Als ich zu einem x86-basierten Computer gezwungen wurde und anfing, einen Blick darauf zu werfen (mit m68k-Hintergrund), fühlte ich mich als frustrierend beim Programmieren ... als hätte ich das Programmieren mit einer Sprache wie C gelernt und wäre es dann gezwungen, mit asm in Kontakt zu treten ... Sie "fühlen", dass Sie Ausdruckskraft, Leichtigkeit, Klarheit, "Kohärenz", "Intuitionierbarkeit" verlieren. Ich bin sicher, wenn ich mit x86 mit der asm-Programmierung begonnen hätte, hätte ich gedacht es ist nicht so schlimm ... vielleicht ... ich habe auch MMIX und MIPS gemacht und ihr "asm lang" ist weitaus besser als x86 (wenn dies der richtige PoV für das Q ist, aber vielleicht nicht)
ShinTakezou

Das Adressierungsmodusproblem wurde im 80386 behoben. Nur 16-Bit-Code hat begrenzte Adressierungsmodi, 32-Bit-Code ist viel besser. Sie können die 32-Bit-Adressierungsmodi in 16-Bit-Code mit einem speziellen Präfix abrufen und umgekehrt.
Fuz

@FUZxxl: Ja ... ich hätte wahrscheinlich erwähnen sollen, dass die Hässlichkeit hauptsächlich auf 16-Bit-Code beschränkt ist. Behoben (ich denke). :)
CHao

Die wahrgenommene Uneleganz beruht hauptsächlich auf dem Missverständnis, dass die Register eines 8086 Allzweckregister sind; das ist falsch Jeder von ihnen hat einen besonderen Zweck und wenn Sie sich nicht an ihre Zwecke halten, werden Sie eine schlechte Zeit haben.
Fuz

3

Ich bin kein Experte, aber es scheint, dass viele der Funktionen, warum die Leute es nicht mögen, die Gründe dafür sein können, dass es gut funktioniert. Vor einigen Jahren wurden Register (anstelle eines Stapels), Registerrahmen usw. als gute Lösungen angesehen, um die Architektur für den Menschen einfacher erscheinen zu lassen. Heutzutage kommt es jedoch auf die Cache-Leistung an, und mit den Wörtern variabler Länge von x86 können mehr Anweisungen im Cache gespeichert werden. Die "Anweisungsdecodierung", auf die die Gegner meines Erachtens einmal die Hälfte des Chips aufgenommen haben, ist bei weitem nicht mehr so.

Ich denke, Parallelität ist heutzutage einer der wichtigsten Faktoren - zumindest für Algorithmen, die bereits schnell genug laufen, um verwendet werden zu können. Durch das Ausdrücken einer hohen Parallelität in der Software kann die Hardware Speicherlatenzen amortisieren (oder häufig vollständig verbergen). Natürlich liegt die weiterreichende Architekturzukunft wahrscheinlich in so etwas wie Quantencomputing.

Ich habe von nVidia gehört, dass einer der Fehler von Intel darin bestand, dass die Binärformate nahe an der Hardware gehalten wurden. Die PTX von CUDA führt einige Berechnungen zur schnellen Verwendung von Registern durch (Diagrammfärbung), sodass nVidia einen Registermaschinen anstelle eines Stapelcomputers verwenden kann, aber dennoch über einen Upgrade-Pfad verfügt, der nicht die gesamte alte Software beschädigt.


9
RISC wurde nicht für menschliche Entwickler entwickelt. Eine der Ideen hinter RISC war es, einen Teil der Komplexität des Chips auf denjenigen zu verlagern, der die Assembly geschrieben hat, idealerweise auf den Compiler. Mehr Register bedeuteten weniger Speicherbedarf und weniger Abhängigkeiten zwischen Befehlen, was tiefere Pipelines und eine höhere Leistung ermöglichte. Beachten Sie, dass x86-64 doppelt so viele allgemeine Register hat wie x86, und dies allein ist für signifikante Leistungssteigerungen verantwortlich. Anweisungen für die meisten x86-Chips werden vor dem Zwischenspeichern dekodiert, nicht danach (die Größe spielt hier also keine Rolle).
Dietrich Epp

3
@Dietrich Epp: Das stimmt nicht ganz. Auf dem x86-64 sind zwar mehr Register in der ISA sichtbar, aber moderne x86-Implementierungen verfügen normalerweise über eine Registerdatei im RISC-Stil, die bei Bedarf den Registern der ISA zugeordnet wird, um die Ausführung zu beschleunigen.
Billy ONeal

"Ich habe von nVidia gehört, dass einer der Fehler von Intel darin bestand, dass die Binärformate nahe an der Hardware gehalten wurden." - Ich habe das und den PTX-Teil der CUDA nicht bekommen.
Krallen

1
@Dietrech Epp: "Und Anweisungen auf den meisten x86-Chips werden dekodiert, bevor sie zwischengespeichert werden, nicht nach" Das stimmt nicht. Sie werden zwischengespeichert, bevor sie dekodiert werden. Ich glaube, der Pentium 4 hatte einen zusätzlichen Trace-Cache, der nach dem Dekodieren zwischengespeichert wurde, aber dieser wurde eingestellt.
Nathan Fellman

das ist nicht wahr, die neuesten "sandy bridge" -Prozessoren verwenden eine Art Trace-Cache (wie das für Pentium 4, oh dieser alte Junge: D), also gehen Technologien weg und kommen zurück ...
Quonux

3

Neben den Gründen, die die Leute bereits erwähnt haben:

  • x86-16 hatte ein ziemlich seltsames Speicheradressierungsschema, das es ermöglichte, einen einzelnen Speicherort auf bis zu 4096 verschiedene Arten zu adressieren, den Arbeitsspeicher auf 1 MB zu beschränken und Programmierer zu zwingen, mit zwei verschiedenen Zeigergrößen umzugehen. Glücklicherweise machte die Umstellung auf 32-Bit diese Funktion unnötig, aber x86-Chips tragen immer noch die Menge der Segmentregister.
  • Obwohl es nicht ein Fehler von x86 per se , x86 Aufrufkonventionen nicht wie MIPS standardisiert war (vor allem , weil MS-DOS nicht mit Compiler gekommen), uns mit dem Chaos zu verlassen __cdecl, __stdcall, __fastcallusw.

Hmm .. wenn ich an x86-Konkurrenten denke, denke ich nicht an MIPS. ARM oder PowerPC vielleicht ...
Billy ONeal

@ Billy: x86 gibt es schon immer. Zu einer Zeit war MIPS ein x86-Konkurrent. Wie ich mich erinnere, wurde die Arbeit von x86 eingestellt, um ein Niveau zu erreichen, in dem es mit MIPS konkurrenzfähig war. (Damals, als MIPS und SPARC in der Workstation-Arena gegeneinander antraten.)
Shannon Severance

@ Shannon Severance: Nur weil etwas einmal war, heißt das nicht etwas, was ist.
Billy ONeal

2
@supercat: Was die Leute in der Ära des flachen x86-32-Speichermodells gerne vergessen, ist, dass 16 Bit 64 KB Speicher bedeuten (jeder, der sich die Mühe macht, zu rechnen, wird verstehen, dass Magie nicht möglich ist, dass der 8086 kein war böse Bestrafung für ahnungslose Programmierer). Es gibt nur wenige Möglichkeiten, um 64.000 zu erreichen, aber die 8086-Lösung war ein guter Kompromiss.
Olof Forshell

2
@OlofForshell: Ich denke, viele Leute beklagten die Tatsache, dass der 8086 nicht so schön war wie der 68000 (der einen linearen Adressraum von 16 MB und einen freien Weg zu 4 Gigs hatte). Sicherlich wird der Zugriff auf einen 32-Bit-Prozessor den Zugriff auf mehr als 64 KB erleichtern, aber der 8086 ist eine 16-Bit-Architektur, die als Fortschritt gegenüber dem 8-Bit-8080 konzipiert wurde. Ich sehe keinen Grund, warum Intel hätte springen sollen direkt von einem 8-Bit zu einem 32-Bit.
Supercat

3

Ich denke, Sie werden einen Teil der Antwort erhalten, wenn Sie jemals versuchen, einen Compiler zu schreiben, der auf x86 abzielt, oder wenn Sie einen x86-Maschinenemulator schreiben oder sogar wenn Sie versuchen, die ISA in einem Hardware-Design zu implementieren.

Obwohl ich verstehe, dass "x86 hässlich ist!" Argumente, ich denke immer noch, dass es mehr Spaß macht , x86-Assembly zu schreiben als MIPS (zum Beispiel) - letzteres ist einfach nur langweilig. Es sollte immer eher für Compiler als für Menschen nett sein. Ich bin mir nicht sicher, ob ein Chip Compiler-Autoren feindlicher gegenüberstehen könnte, wenn er es versuchen würde ...

Der hässlichste Teil für mich ist die Art und Weise, wie die Segmentierung (im Real-Modus) funktioniert - dass jede physikalische Adresse 4096 Segmente hat: Offset-Aliase. Wann hast du das zuletzt gebraucht ? Die Dinge wären viel einfacher gewesen, wenn der Segmentteil streng höherwertige Bits einer 32-Bit-Adresse wären.


m68k ist viel lustiger und für Menschen weitaus netter als x86 (was vielen m68k-Programmierern nicht so "menschlich" erscheinen kann), wenn der richtige PoV die Art ist, wie Menschen Code in diese Assembly schreiben können.
ShinTakezou

Das Segment: Offset-Adressierung war ein Versuch, in gewissem Maße mit der CP / M-Welt kompatibel zu bleiben. Eine der schlimmsten Entscheidungen aller Zeiten.
Turing Complete

@Turing Complete: Segment: Offset war NICHT in erster Linie ein Versuch, mit der CP / M-Welt kompatibel zu bleiben. Es war ein sehr erfolgreicher Versuch, einem 16-Bit-Prozessor zu ermöglichen, mehr als 64 KByte zu adressieren, indem Code, Daten, Stapel und andere Speicherbereiche in verschiedenen Segmenten platziert wurden.
Olof Forshell

1
In Wirklichkeit war das Platzieren von Daten und Stapeln in verschiedenen Segmenten für C völlig nutzlos. es war nur für asm verwendbar. In C kann ein Zeiger auf Daten mit statischer, automatischer oder dynamisch zugewiesener Speicherdauer verweisen, sodass das Segment nicht entfernt werden kann. Vielleicht war es nützlich für Pascal oder Fortran oder so, aber nicht für C, das zu dieser Zeit bereits die dominierende Sprache war ...
R .. GitHub STOP HELPING ICE

2
@Bernd: Der Grund, warum fs / gs für die threadlokale Speicherung ausgewählt wurden, ist nicht, dass Segmentregister dafür gut sind. Es ist nur so, dass x86 ernsthaft ausgehungert ist und die Segmentregister nicht verwendet wurden. Ein Allzweckregister, das auf die Thread-Struktur zeigt, hätte genauso gut funktioniert, und tatsächlich verwenden viele RISC-Systeme mit mehr Registern eines als Thread-Zeiger.
R .. GitHub STOP HELPING ICE

1
  1. x86 verfügt über einen sehr, sehr begrenzten Satz von Allzweckregistern

  2. Es fördert einen sehr ineffizienten Entwicklungsstil auf der untersten Ebene (CISC-Hölle) anstelle einer effizienten Lade- / Speichermethode

  3. Intel traf die schreckliche Entscheidung, das einfach dumme Segment- / Offset-Speicheradressierungsmodell einzuführen, um mit (bereits zu diesem Zeitpunkt!) Veralteter Technologie kompatibel zu bleiben

  4. Zu einer Zeit, als alle 32-Bit-CPUs betrieben, hielt der x86 die Mainstream-PC-Welt zurück, indem er eine magere 16-Bit-CPU (die meisten von ihnen - der 8088 - sogar nur mit externen 8-Bit-Datenpfaden, was sogar noch beängstigender ist!) War


Für mich (und ich bin ein DOS-Veteran, der jede Generation von PCs aus Entwicklersicht gesehen hat!) War Punkt 3 das Schlimmste.

Stellen Sie sich die folgende Situation vor, die wir in den frühen 90ern hatten (Mainstream!):

a) Ein Betriebssystem, das aus alten Gründen verrückte Einschränkungen hatte (640 KB leicht zugänglicher RAM) - DOS

b) Eine Betriebssystemerweiterung (Windows), die mehr RAM leisten konnte, aber in Bezug auf Spiele usw. begrenzt war und nicht die stabilste Sache der Welt war (zum Glück änderte sich dies später, aber ich spreche hier über die frühen 90er Jahre)

c) Die meiste Software war noch DOS und wir mussten häufig Bootdisketten für spezielle Software erstellen, da es diese EMM386.exe gab, die einige Programme mochten, andere hassten (insbesondere Gamer - und ich war zu dieser Zeit ein AVID-Spieler - wissen, was ich rede hier)

d) Wir waren auf MCGA 320x200x8 Bits beschränkt (ok, es gab ein bisschen mehr mit speziellen Tricks, 360x480x8 war möglich, aber nur ohne Unterstützung der Laufzeitbibliothek), alles andere war chaotisch und schrecklich ("VESA" - lol)

e) In Bezug auf die Hardware hatten wir 32-Bit-Maschinen mit einigen Megabyte RAM und VGA-Karten mit einer Unterstützung von bis zu 1024 x 768

Grund für diese schlechte Situation?

Eine einfache Designentscheidung von Intel. Kompatibilität der Maschinenanweisungsebene (NICHT Binärstufe!) Mit etwas, das bereits im Sterben lag. Ich glaube, es war die 8085. Die anderen, scheinbar nicht zusammenhängenden Probleme (Grafikmodi usw.) waren aus technischen Gründen und aufgrund der sehr engen Probleme verbunden aufgeschlossene Architektur brachte die x86-Plattform mit sich.

Heutzutage ist die Situation anders, aber fragen Sie jeden Assembler-Entwickler oder Leute, die Compiler-Backends für x86 erstellen. Die wahnsinnig geringe Anzahl von Allzweckregistern ist nichts anderes als ein schrecklicher Leistungskiller.


Das einzige große Problem mit der segmentierten 8086-Architektur bestand darin, dass es nur ein nicht dediziertes Segmentregister (ES) gab und dass die Programmiersprachen nicht dafür ausgelegt waren, effektiv damit zu arbeiten. Der verwendete Stil der skalierten Adressierung würde in einer objektorientierten Sprache sehr gut funktionieren, in der nicht erwartet wird, dass Objekte an beliebigen Adressen beginnen können (wenn Objekte an Absatzgrenzen ausgerichtet werden, müssen Objektreferenzen nur zwei Bytes und nicht mehr sein vier). Wenn man frühen Macintosh-Code mit PC-Code vergleicht, sieht der 8086 im Vergleich zu
68000

@supercat: Eigentlich war das es-Register etwas gewidmet, nämlich den Zeichenfolgenanweisungen, die gespeichert (movs, stos) oder gescannt werden mussten (cmps und scas). Bei einer Adressierung von 64 KB aus jedem Segmentregister wurde auch die "fehlende Verbindung" zu einem anderen Speicher als Code, Daten und Stapelspeicher (cs, ds, ss) bereitgestellt. Die Segmentregister stellten eine Art Speicherschutzschema bereit, bei dem Sie nicht außerhalb der 64-KB-Speicherblöcke der Register adressieren konnten. Welche bessere Lösung schlagen Sie vor, da das x86 eine 16-Bit-Architektur und die Lithografie-Einschränkungen des Tages war?
Olof Forshell

@OlofForshell: ES wurde für Zeichenfolgenanweisungen verwendet, konnte jedoch als nicht festgeschriebenes Register für Code verwendet werden, der diese nicht verwendet. Eine Möglichkeit, den Seg-Reg-Engpass zu beseitigen, ohne zu viel Opcode-Speicherplatz zu benötigen, wäre ein "rseg" -Präfix, das angibt, dass für die folgende Anweisung im R / M-Format das Feld "r" aus CS / SS / DS ausgewählt wird / ES / FS / GS / ?? / ?? anstelle von AX / BX / CX / DX / SI / DI / SP / BP und mit Präfixen für FS / GS und Anweisungen für LFS und LGS (wie LDS und LES). Ich weiß nicht, wie die Mikroarchitektur für den 8086 aufgebaut war, aber ich würde denken, dass so etwas hätte funktionieren können.
Supercat

@supercat: Wie ich schrieb, "stellen Register auch die fehlende Verbindung zum Speicher bereit, außer ..." Fs und gs kamen, wie ich mich erinnere, erst beim 386 an.
Olof Forshell

1
@OlofForshell: Das haben sie nicht, was die 80286-Architektur in den meisten Punkten noch schlechter machte als die 8086-Architektur. Mein Punkt war, dass das Hinzufügen von ein paar weiteren Segmentregistern (oder sogar eines) die 8086-Architektur viel nützlicher gemacht hätte, und der Befehlssatz hätte sauberer und nützlicher sein können, wenn auf Segmentregister ähnlich wie auf zugegriffen werden könnte andere.
Supercat
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.