Kürzlich habe ich einige SO-Archive gelesen und bin auf Aussagen gegen die x86-Architektur gestoßen.
Warum benötigen wir eine andere CPU-Architektur für Server & Mini / Mainframe & Mixed-Core? sagt
" PC-Architektur ist ein Chaos, das würde Ihnen jeder Betriebssystementwickler sagen. "Lohnt sich das Erlernen der Assemblersprache?( archiviert ) sagt
" Erkenne, dass die x86-Architektur bestenfalls schrecklich ist "Gibt es eine einfache Möglichkeit, den x86-Assembler zu erlernen? sagt
" Die meisten Colleges unterrichten Montage auf so etwas wie MIPS, weil es viel einfacher zu verstehen ist, dass x86-Montage wirklich hässlich ist. "
und viele weitere Kommentare wie
"Im Vergleich zu den meisten Architekturen ist X86 ziemlich schlecht."
" Es ist definitiv die übliche Weisheit, dass X86 MIPS, SPARC und PowerPC unterlegen ist. "
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?
Antworten:
Einige mögliche Gründe dafür:
IN
undOUT
)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
MOVSB
Befehl einen relativ großen Block C-Code, um zu beschreiben, was er tut: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.
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.
quelle
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.
quelle
add
nach der anderen auszuführenadd
. 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.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.
quelle
Ich habe hier einige zusätzliche Aspekte:
Betrachten Sie die Operation "a = b / c" x86 würde dies als implementieren
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:
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".
quelle
there typically won't be a reminder
aber Wiki sagt, dass Mips es haben: en.wikipedia.org/wiki/MIPS_instruction_set#IntegerIch 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:
mov %eax, 0x1c(%esp,%edi,4)
Adressierungsmodi, die nicht aufgeschlüsselt sind.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.
quelle
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.
quelle
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.
quelle
Neben den Gründen, die die Leute bereits erwähnt haben:
__cdecl
,__stdcall
,__fastcall
usw.quelle
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.
quelle
x86 verfügt über einen sehr, sehr begrenzten Satz von Allzweckregistern
Es fördert einen sehr ineffizienten Entwicklungsstil auf der untersten Ebene (CISC-Hölle) anstelle einer effizienten Lade- / Speichermethode
Intel traf die schreckliche Entscheidung, das einfach dumme Segment- / Offset-Speicheradressierungsmodell einzuführen, um mit (bereits zu diesem Zeitpunkt!) Veralteter Technologie kompatibel zu bleiben
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.
quelle