Récemment, j'ai lu quelques archives SO et rencontré des déclarations sur l'architecture x86.
Pourquoi avons-nous besoin d'une architecture CPU différente pour le serveur et le mini/mainframe et le core mixte? dit
" L'architecture PC est un gâchis, tout développeur de système d'exploitation vous le dirait. "
Est-ce que l'apprentissage du langage d'assemblage en vaut la peine? ( archivé ) dit
" Réalisez que l'architecture x86 est au mieux horrible "
ne façon simple d'apprendre l'assembleur x86? dit
" La plupart des collèges enseignent l'assemblage sur quelque chose comme MIPS car c'est beaucoup plus simple à comprendre, l'assemblage x86 est vraiment moche "
et bien d'autres commentaires comme
"Par rapport à la plupart des architectures, X86 craint assez mal."
" C'est certainement la sagesse conventionnelle que X86 est inférieur à MIPS, SPARC et PowerPC "
J'ai essayé de chercher mais je n'ai trouvé aucune raison. Je ne trouve pas x86 mauvais probablement parce que c'est la seule architecture que je connaisse.
Quelqu'un peut-il me donner des raisons de considérer x86 laid/mauvais/inférieur par rapport aux autres.
Quelques raisons possibles:
IN
et OUT
)le code d'assemblage x86 est compliqué car x86 est une architecture compliquée avec de nombreuses fonctionnalités. Une liste d'instructions pour une machine MIPS typique tient sur une seule feuille de papier de format lettre. La liste équivalente pour x86 remplit plusieurs pages et les instructions en font plus, vous avez donc souvent besoin d'une explication plus détaillée de ce qu'elles font qu'une liste ne peut fournir. Par exemple, l'instruction MOVSB
a besoin d'un bloc de code C relativement grand pour décrire ce qu'elle fait:
if (DF==0)
*(byte*)DI++ = *(byte*)SI++;
else
*(byte*)DI-- = *(byte*)SI--;
Il s'agit d'une instruction unique effectuant un chargement, un magasin et deux additions ou soustractions (contrôlées par une entrée d'indicateur), chacune étant des instructions distinctes sur une machine RISC.
Bien que la simplicité de MIPS (et des architectures similaires) ne les rend pas nécessairement supérieures, pour enseigner une introduction à la classe assembleur, il est logique de commencer par une plus simple ISA . Certaines classes d'assemblage enseignent un sous-ensemble ultra-simplifié de x86 appelé y86 , qui est simplifié au-delà du point de ne pas être utile pour une utilisation réelle (par exemple, aucune instruction de décalage), ou certains n'enseignent que les instructions de base x86.
EDIT: Ce n'est pas censé être un bash le parti x86! . Je n'avais pas d'autre choix que de dénigrer un peu compte tenu de la formulation de la question. Mais à l'exception de (1), toutes ces choses ont été faites pour de bonnes raisons (voir commentaires). Les concepteurs d'Intel ne sont pas stupides - ils voulaient réaliser certaines choses avec leur architecture, et ce sont certaines des taxes qu'ils ont dû payer pour que ces choses deviennent réalité.
Le principal coup contre x86 dans mon esprit est ses origines CISC - le jeu d'instructions contient beaucoup d'interdépendances implicites. Ces interdépendances rendent difficile la réorganisation des instructions sur la puce, car les artefacts et la sémantique de ces interdépendances doivent être préservés pour chaque instruction.
Par exemple, la plupart des instructions d'ajout et de soustraction d'entiers x86 modifient le registre des indicateurs. Après avoir effectué un ajout ou une soustraction, l'opération suivante consiste souvent à regarder le registre des indicateurs pour vérifier le dépassement, le bit de signature, etc. S'il y a un autre ajout après cela, il est très difficile de dire s'il est sûr de commencer l'exécution du 2e ajout. avant que le résultat du 1er ajout soit connu.
Sur une architecture RISC, l'instruction add spécifierait les opérandes d'entrée et le (s) registre (s) de sortie, et tout ce qui concerne l'opération se déroulerait en utilisant uniquement ces registres. Cela rend beaucoup plus facile le découplage des opérations d'ajout qui sont proches les unes des autres car il n'y a pas de registre de drapeaux bloomin 'forçant tout à s'aligner et à exécuter un seul fichier.
La puce DEC Alpha AXP, une conception RISC de style MIPS, était douloureusement spartiate dans les instructions disponibles, mais le jeu d'instructions a été conçu pour éviter les dépendances de registre implicites entre instructions. Il n'y avait pas de registre de pile défini par le matériel. Il n'y avait pas de registre d'indicateurs définis par le matériel. Même le pointeur d'instruction était défini par le système d'exploitation - si vous vouliez retourner vers l'appelant, vous deviez déterminer comment l'appelant allait vous faire savoir à quelle adresse retourner. Cela était généralement défini par la convention d'appel du système d'exploitation. Sur le x86, cependant, il est défini par le matériel de la puce.
Quoi qu'il en soit, sur 3 ou 4 générations de conceptions de puces Alpha AXP, le matériel est passé d'une implémentation littérale du jeu d'instructions spartiate avec 32 registres int et 32 registres flottants à un moteur d'exécution massivement hors service avec 80 registres internes, renommage des registres, transmission des résultats (où le résultat d'une instruction précédente est transmis à une instruction ultérieure qui dépend de la valeur) et toutes sortes de boosters de performances sauvages et fous. Et avec toutes ces cloches et sifflets, la puce à puce AXP était encore beaucoup plus petite que la puce à puce Pentium comparable de l'époque, et l'AXP était beaucoup plus rapide.
Vous ne voyez pas ces types de rafales de performances qui améliorent les choses dans l'arbre généalogique x86 en grande partie parce que la complexité du jeu d'instructions x86 rend de nombreux types d'optimisations d'exécution prohibitifs, voire impossibles. Le coup de génie d'Intel a été d'abandonner la mise en œuvre du jeu d'instructions x86 dans le matériel - toutes les puces x86 modernes sont en fait des cœurs RISC qui, dans une certaine mesure, interprètent les instructions x86, les traduisant en microcode interne qui préserve toute la sémantique du x86 d'origine , mais permet un peu de ce RISC dans le désordre et d'autres optimisations sur le microcode.
J'ai écrit beaucoup d'assembleur x86 et je peux pleinement apprécier la commodité de ses racines CISC. Mais je n'ai pas vraiment compris à quel point x86 était compliqué jusqu'à ce que je passe du temps à écrire l'assembleur Alpha AXP. J'ai été stupéfait par la simplicité et l'uniformité d'AXP. Les différences sont énormes et profondes.
L'architecture x86 date de la conception du microprocesseur 8008 et de ses proches. Ces processeurs ont été conçus à une époque où la mémoire était lente et si vous pouviez le faire sur le processeur, c'était souvent beaucoup plus rapide. Cependant, le die-space CPU était également cher. Ces deux raisons expliquent pourquoi il n'y a qu'un petit nombre de registres qui ont tendance à avoir des objectifs particuliers, et un ensemble d'instructions compliqué avec toutes sortes de pièges et de limitations.
D'autres processeurs de la même époque (par exemple la famille 6502) ont également des limitations et des bizarreries similaires. Fait intéressant, les séries 8008 et 6502 étaient conçues comme des contrôleurs intégrés. Même à l'époque, les contrôleurs intégrés devaient être programmés dans l'assembleur et, à bien des égards, adaptés au programmeur d'assemblage plutôt qu'au rédacteur du compilateur. (Regardez la puce VAX pour savoir ce qui se passe lorsque vous répondez à l'écriture du compilateur.) Les concepteurs ne s'attendaient pas à ce qu'ils deviennent des plates-formes informatiques à usage général; c'est à cela que servaient les choses comme les prédécesseurs de l'archivage POWER. La révolution des ordinateurs personnels a bien sûr changé cela.
J'ai ici quelques aspects supplémentaires:
Considérez que l'opération "a = b/c" x86 implémenterait ceci comme
mov eax,b
xor edx,edx
div dword ptr c
mov a,eax
Comme bonus supplémentaire de l'instruction div, edx contiendra le reste.
Un processeur RISC devrait d'abord charger les adresses de b et c, charger b et c de la mémoire dans les registres, faire la division et charger l'adresse de a puis stocker le résultat. Dst, syntaxe src:
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
Ici, il n'y aura généralement pas de reste.
Si des variables doivent être chargées via des pointeurs, les deux séquences peuvent devenir plus longues bien que cela soit moins possible pour le RISC car il peut avoir un ou plusieurs pointeurs déjà chargés dans un autre registre. x86 a moins de registre, donc la probabilité que le pointeur se trouve dans l'un d'eux est plus petite.
Avantages et inconvénients:
Les instructions RISC peuvent être mélangées avec du code environnant pour améliorer la planification des instructions, c'est moins une possibilité avec x86 qui fait plutôt ce travail (plus ou moins bien selon la séquence) à l'intérieur du CPU lui-même. La séquence RISC ci-dessus aura généralement une longueur de 28 octets (7 instructions de largeur 32 bits/4 octets chacune) sur une architecture 32 bits. Cela entraînera davantage la mémoire hors puce lors de la récupération des instructions (sept récupérations). La séquence x86 plus dense contient moins d'instructions et bien que leurs largeurs varient, vous regardez probablement également une moyenne de 4 octets/instruction. Même si vous avez des caches d'instructions pour accélérer cela, sept récupérations signifient que vous aurez un déficit de trois ailleurs à rattraper par rapport au x86.
L'architecture x86 avec moins de registres à sauvegarder/restaurer signifie qu'elle effectuera probablement des commutateurs de threads et gérera les interruptions plus rapidement que RISC. Plus de registres à sauvegarder et à restaurer nécessitent plus d'espace de pile temporaire RAM pour faire des interruptions et plus d'espace de pile permanent pour stocker les états des threads. Ces aspects devraient faire de x86 un meilleur candidat pour exécuter RTOS pur.
Sur une note plus personnelle, je trouve qu'il est plus difficile d'écrire un assemblage RISC que x86. Je résous cela en écrivant la routine RISC en C, en compilant et en modifiant le code généré. Ceci est plus efficace du point de vue de la production de code et probablement moins efficace du point de vue de l'exécution. Tous ces 32 registres à suivre. Avec x86, c'est l'inverse: 6-8 registres avec de "vrais" noms rendent le problème plus gérable et donnent plus de confiance que le code produit fonctionnera comme prévu.
Laid? C'est dans l'œil du spectateur. Je préfère "différent".
Je pense que cette question a une fausse hypothèse. Ce sont principalement des universitaires obsédés par RISC qui appellent x86 laid. En réalité, le x86 ISA peut faire en une seule instruction des opérations qui prendraient 5-6 instructions sur les ISC RISC. Les fans de RISC peuvent contrer que les processeurs x86 modernes décomposent ces instructions "complexes" en microops) ; pourtant:
mov %eax, 0x1c(%esp,%edi,4)
c'est-à-dire les modes d'adressage, et ceux-ci ne sont pas décomposés.x86 a vraiment absorbé tous les bons aspects de RISC il y a environ 10-15 ans, et les qualités restantes de RISC (en fait celle définissant ) - le jeu d'instructions minimal ) sont nuisibles et indésirables.
Mis à part le coût et la complexité de la fabrication de processeurs et leurs besoins en énergie, x86 est le meilleur ISA. Quiconque vous dit le contraire laisse l'idéologie ou l'agenda entraver son raisonnement.
D'un autre côté, si vous ciblez des appareils embarqués où le coût du CPU compte, ou des appareils embarqués/mobiles où la consommation d'énergie est une préoccupation majeure, ARM ou MIPS a probablement plus de sens. Gardez à l'esprit, bien que vous ayez encore à faire face à la mémoire RAM et à la taille binaire supplémentaires nécessaires pour gérer du code qui est facilement 3 à 4 fois plus grand, et vous ne pourrez pas vous rapprocher des performances. vous allez courir dessus.
le langage assembleur x86 n'est pas si mal. C'est lorsque vous arrivez au code machine qu'il commence à devenir vraiment moche. Les encodages d'instructions, les modes d'adressage, etc. sont beaucoup plus compliqués que ceux de la plupart des CPU RISC. Et il y a un plaisir supplémentaire intégré à des fins de compatibilité descendante - des choses qui ne se déclenchent que lorsque le processeur est dans un certain état.
Dans les modes 16 bits, par exemple, l'adressage peut sembler carrément bizarre; il y a un mode d'adressage pour [BX+SI]
, mais pas pour [AX+BX]
. Des choses comme ça ont tendance à compliquer l'utilisation du registre, car vous devez vous assurer que votre valeur est dans un registre que vous pouvez utiliser comme vous le souhaitez.
(Heureusement, le mode 32 bits est beaucoup plus sain (bien qu'il soit parfois un peu bizarre - segmentation par exemple), et le code x86 16 bits est largement hors de propos en dehors des chargeurs de démarrage et de certains environnements intégrés.)
Il y a aussi les restes de l'ancien temps, quand Intel essayait de faire de x86 le processeur ultime. Instructions de quelques octets de long qui exécutaient des tâches que personne ne fait plus, car elles étaient franchement trop bizarres, lentes ou compliquées. Les ENTRER et instructions LOOP , pour deux exemples - notez que le code de trame de pile C est comme "Push ebp; mov ebp, esp" et non "enter" pour la plupart des compilateurs.
Je ne suis pas un expert, mais il semble que de nombreuses fonctionnalités pour lesquelles les gens ne l'aiment pas peuvent être les raisons pour lesquelles il fonctionne bien. Il y a plusieurs années, avoir des registres (au lieu d'une pile), des trames de registre, etc. était considéré comme de belles solutions pour rendre l'architecture plus simple aux humains. Cependant, de nos jours, ce qui compte, ce sont les performances du cache, et les mots de longueur variable de x86 lui permettent de stocker plus d'instructions dans le cache. Le "décodage des instructions", qui, je crois, a fait remarquer aux opposants une fois occupé la moitié de la puce, n'est plus du tout le même.
Je pense que le parallélisme est l'un des facteurs les plus importants de nos jours - du moins pour les algorithmes qui fonctionnent déjà assez rapidement pour être utilisables. L'expression d'un parallélisme élevé dans le logiciel permet au matériel d'amortir (ou souvent de masquer complètement) les latences de mémoire. Bien sûr, l'avenir de l'architecture le plus éloigné se situe probablement dans quelque chose comme l'informatique quantique.
J'ai entendu de nVidia que l'une des erreurs d'Intel était de garder les formats binaires proches du matériel. Le PTX de CUDA effectue des calculs rapides d'utilisation des registres (coloration des graphiques), donc nVidia peut utiliser une machine à registres au lieu d'une machine à pile, mais a toujours un chemin de mise à niveau qui ne casse pas tous les anciens logiciels.
Je pense que vous obtiendrez une partie de la réponse si vous essayez d'écrire un compilateur qui cible x86, ou si vous écrivez un émulateur de machine x86, ou même si vous essayez d'implémenter le ISA dans une conception matérielle.
Bien que je comprenne le "x86 est moche!" arguments, je pense toujours que c'est plus amusant écrire un assemblage x86 que MIPS (par exemple) - ce dernier est tout simplement fastidieux. Il a toujours été conçu pour être agréable aux compilateurs plutôt qu'aux humains. Je ne suis pas sûr qu'une puce pourrait être plus hostile aux auteurs de compilateurs si elle essayait ...
La partie la plus laide pour moi est le fonctionnement de la segmentation (en mode réel) - que toute adresse physique a un segment 4096: alias de décalage. Quand avez-vous besoin pour la dernière fois? Les choses auraient été tellement plus simples si la partie segment était des bits strictement supérieurs d'une adresse 32 bits.
Outre les raisons que les gens ont déjà mentionnées:
__cdecl
, __stdcall
, __fastcall
, etc.x86 possède un ensemble très, très limité de registres à usage général
il favorise un style de développement très inefficace au niveau le plus bas (enfer CISC) au lieu d'une méthodologie de chargement/stockage efficace
Intel a pris la décision horrible d'introduire le modèle d'adressage de segment/offset clairement stupide pour rester compatible avec (pour le moment déjà!) Une technologie obsolète
À une époque où tout le monde passait au 32 bits, le x86 a freiné le monde des PC traditionnels en étant un maigre 16 bits (la plupart d'entre eux - le 8088 - même uniquement avec des chemins de données externes 8 bits, ce qui est encore plus effrayant!)
Pour moi (et je suis un vétéran du DOS qui a vu chaque génération de PC du point de vue des développeurs!), Le point 3. était le pire.
Imaginez la situation suivante que nous avions au début des années 90 (grand public!):
a) Un système d'exploitation qui avait des limitations insensées pour des raisons héritées (640 Ko de RAM facilement accessible) - DOS
b) Une extension du système d'exploitation (Windows) qui pourrait faire plus en termes de RAM, mais était limitée en ce qui concerne les jeux, etc. ... et n'était pas la chose la plus stable sur Terre (heureusement, cela a changé plus tard, mais j'ai je parle du début des années 90 ici)
c) La plupart des logiciels étaient encore DOS et nous devions souvent créer des disques de démarrage pour des logiciels spéciaux, car il y avait ce EMM386.exe que certains programmes aimaient, d'autres détestaient (en particulier les joueurs - et j'étais un joueur AVID à l'époque - je sais ce que je je parle ici)
d) Nous étions limités à MCGA 320x200x8 bits (ok, il y en avait un peu plus avec des astuces spéciales, 360x480x8 était possible, mais seulement sans le support de la bibliothèque d'exécution), tout le reste était désordonné et horrible ("VESA" - lol)
e) Mais en termes de matériel, nous avions des machines 32 bits avec quelques mégaoctets de RAM et des cartes VGA avec une prise en charge jusqu'à 1024x768
Raison de cette mauvaise situation?
Une simple décision de conception d'Intel. Compatibilité du niveau d'instruction machine (PAS au niveau binaire!) Avec quelque chose qui était déjà en train de mourir, je pense que c'était le 8085. Les autres problèmes apparemment sans rapport (modes graphiques, etc ...) étaient liés pour des raisons techniques et en raison de l'étroitesse architecture orientée la plate-forme x86 a apporté avec elle-même.
Aujourd'hui, la situation est différente, mais demandez à tout développeur assembleur ou à tous ceux qui construisent des backends de compilateur pour le x86. Le nombre incroyablement bas de registres à usage général n'est rien d'autre qu'un horrible tueur de performances.