web-dev-qa-db-fra.com

Pourquoi x86 est-il moche? Pourquoi est-il considéré comme inférieur par rapport aux autres?

Récemment, j'ai lu quelques archives SO et rencontré des déclarations sur l'architecture x86.

et bien d'autres commentaires comme

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.

98
claws

Quelques raisons possibles:

  1. x86 est un relativement ancien ISA (ses progéniteurs étaient 8086, après tout)
  2. x86 a évolué de manière significative à plusieurs reprises, mais le matériel est nécessaire pour maintenir la compatibilité descendante avec les anciens binaires. Par exemple, le matériel x86 moderne contient toujours la prise en charge de l'exécution native de code 16 bits. De plus, plusieurs modèles d'adressage de mémoire existent pour permettre à un code plus ancien d'interagir sur le même processeur, comme le mode réel, le mode protégé, le mode 8086 virtuel et le mode long (AMD64). Cela peut être déroutant pour certains.
  3. x86 est une machine CISC. Pendant longtemps, cela a signifié qu'il était plus lent que les machines RISC comme MIPS ou ARM, car les instructions ont interdépendance des données et indicateurs rendant la plupart des formes de parallélisme de niveau d'instruction difficiles à mettre en œuvre. Les implémentations modernes traduisent les instructions x86 en instructions de type RISC appelées " micro-ops " sous les couvertures pour rendre ces types d'optimisations pratiques à implémenter dans le matériel.
  4. À certains égards, le x86 n'est pas inférieur, c'est juste différent. Par exemple, l'entrée/sortie est gérée comme un mappage de mémoire sur la grande majorité des architectures, mais pas sur le x86. (NB: les machines x86 modernes ont généralement une forme de prise en charge DMA et communiquent avec d'autres matériels via le mappage de la mémoire; mais le ISA a toujours des instructions d'E/S comme IN et OUT)
  5. Le x86 ISA a très peu de registres architecturaux, ce qui peut forcer les programmes à effectuer un aller-retour dans la mémoire plus fréquemment qu'il ne serait autrement nécessaire. Les instructions supplémentaires nécessaires pour ce faire nécessitent des ressources d'exécution qui pourraient être consacrées à un travail utile, bien que transfert de magasin efficace maintienne une latence faible. Les implémentations modernes avec le changement de nom de registre sur un grand fichier de registre physique peuvent garder de nombreuses instructions en vol, mais le manque de registres architecturaux était toujours une faiblesse importante pour x86 32 bits. L'augmentation de x86-64 de 8 à 16 registres entiers et vectoriels est l'un des plus grands facteurs du code 64 bits étant plus rapide que 32 bits (avec l'ABI d'appel de registre plus efficace), et non la largeur accrue de chaque registre. Une nouvelle augmentation de 16 à 32 registres entiers en aiderait certains, mais pas autant. (AVX512 augmente cependant à 32 registres vectoriels, car le code à virgule flottante a une latence plus élevée et a souvent besoin de plus de constantes.) ( voir commentaire )
  6. 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.

  7. Le x86 utilise des opcodes de longueur variable, qui ajoutent de la complexité matérielle en ce qui concerne l'analyse des instructions. Dans l'ère moderne, ce coût devient extrêmement faible, car les processeurs deviennent de plus en plus limités par la bande passante mémoire plutôt que par le calcul brut, mais de nombreux articles et attitudes de "basage x86" viennent d'une époque où ce coût était comparativement beaucoup plus élevé.
    Mise à jour 2016: Anandtech a publié un discussion concernant les tailles d'opcode sous x64 et AArch64 .

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é.

85
Billy ONeal

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.

24
dthorpe

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.

19
staticsan

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".

12
Olof Forshell

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:

  1. Dans de nombreux cas, ce n'est que partiellement vrai ou pas vrai du tout. Les instructions "complexes" les plus utiles dans x86 sont des choses comme mov %eax, 0x1c(%esp,%edi,4) c'est-à-dire les modes d'adressage, et ceux-ci ne sont pas décomposés.
  2. Ce qui est souvent plus important sur les machines modernes n'est pas le nombre de cycles dépensés (car la plupart des tâches ne sont pas liées au processeur) mais l'impact du cache d'instructions du code. 5-6 instructions de taille fixe (généralement 32 bits) auront un impact sur le cache beaucoup plus d'une instruction complexe qui est rarement plus de 5 octets.

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.

9
R..

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.

7
cHao

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.

3
gatoatigrado

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.

3
Bernd Jendrissek

Outre les raisons que les gens ont déjà mentionnées:

  • x86-16 avait un assez étrange schéma d'adressage mémoire qui permettait d'adresser un seul emplacement mémoire jusqu'à 4096 façons différentes, limité RAM à 1 Mo, et forcé programmeurs pour gérer deux tailles différentes de pointeurs. Heureusement, le passage au 32 bits a rendu cette fonctionnalité inutile, mais les puces x86 portent toujours la pléthore de registres de segments.
  • Bien que ce ne soit pas une faute de x86 en soi , les conventions d'appel x86 n'étaient pas normalisées comme MIPS (principalement parce que MS-DOS ne venait pas avec des compilateurs ), nous laissant avec le désordre de __cdecl, __stdcall, __fastcall, etc.
2
dan04
  1. x86 possède un ensemble très, très limité de registres à usage général

  2. 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

  3. 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

  4. À 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.

0
Turing Complete