¿Por qué x86 es feo? ¿Por qué se considera inferior en comparación con otros? [cerrado]

105

Recientemente, he estado leyendo algunos archivos SO y encontré declaraciones en contra de la arquitectura x86.

y muchos más comentarios como

Intenté buscar pero no encontré ninguna razón. No encuentro que x86 sea malo probablemente porque esta es la única arquitectura con la que estoy familiarizado.

¿Puede alguien darme razones para considerar x86 feo / malo / inferior en comparación con otros?

garras
fuente
1
Voy con S&A sobre la base de las respuestas hasta ahora, pero señalaré de pasada que CISC no es un problema para el conjunto de instrucciones m68k. x86 es lo que es y puede conservarlo.
dmckee --- ex-moderador gatito
¿Qué es "S&A"? "CISC no es un problema para el conjunto de instrucciones m68k". -- ¿Por qué no?
garras
5
Los chips de la serie motorala 68000 tienen una arquitectura altamente CISC, pero tienen un conjunto de instrucciones uniforme, bastante ortogonal y muy fácil. ¿Por qué la diferencia con x86? No lo sé. Pero tenga en cuenta que hay una gran diferencia entre la complejidad en el chip y la complejidad en el conjunto de instrucciones (es decir, en la interfaz que ve un programador de ensamblaje).
dmckee --- ex-moderador gatito
4
+1 para una pregunta muy interesante.
Turing Complete el
1
Aquí se encuentra un estudio reciente sobre la eficiencia energética de diferentes procesadores, con una buena discusión de lo que impulsó los diseños CISC y RISC. extremetech.com/extreme/…

Respuestas:

93

Un par de posibles razones para ello:

  1. x86 es una ISA relativamente antigua (después de todo, sus progenitores fueron 8086)
  2. x86 ha evolucionado significativamente varias veces, pero se requiere hardware para mantener la compatibilidad con versiones anteriores de archivos binarios. Por ejemplo, el hardware x86 moderno todavía admite la ejecución de código de 16 bits de forma nativa. Además, existen varios modelos de direccionamiento de memoria para permitir que el código antiguo interactúe en el mismo procesador, como el modo real, el modo protegido, el modo 8086 virtual y el modo largo (amd64). Esto puede resultar confuso para algunos.
  3. x86 es una máquina CISC. Durante mucho tiempo, esto significó que era más lento que las máquinas RISC como MIPS o ARM, porque las instrucciones tienen interdependencia de datos y banderas que dificultan la implementación de la mayoría de las formas de paralelismo a nivel de instrucción. Las implementaciones modernas traducen las instrucciones x86 en instrucciones similares a RISC llamadas " micro-ops " debajo de las cubiertas para hacer que este tipo de optimizaciones sea práctico de implementar en hardware.
  4. En algunos aspectos, el x86 no es inferior, solo es diferente. Por ejemplo, la entrada / salida se maneja como mapeo de memoria en la gran mayoría de arquitecturas, pero no en x86. (NB: las máquinas x86 modernas suelen tener algún tipo de soporte DMA y se comunican con otro hardware a través del mapeo de memoria; pero la ISA todavía tiene instrucciones de E / S como INy OUT)
  5. El ISA x86 tiene muy pocos registros de arquitectura, lo que puede obligar a los programas a realizar un recorrido de ida y vuelta a través de la memoria con más frecuencia de lo que sería necesario de otro modo. Las instrucciones adicionales necesarias para hacer esto requieren recursos de ejecución que podrían gastarse en un trabajo útil, aunque un reenvío de tienda eficientemantiene baja la latencia. Las implementaciones modernas con cambio de nombre de registros en un archivo de registro físico grande pueden mantener muchas instrucciones en vuelo, pero la falta de registros arquitectónicos seguía siendo una debilidad significativa para x86 de 32 bits. El aumento de x86-64 de 8 a 16 registros enteros y vectoriales es uno de los factores más importantes para que el código de 64 bits sea más rápido que el de 32 bits (junto con la ABI de llamada de registro más eficiente), no el aumento de ancho de cada registro. Un aumento adicional de 16 a 32 registros enteros ayudaría a algunos, pero no tanto. (Sin embargo, AVX512 aumenta a 32 registros vectoriales porque el código de punto flotante tiene una latencia más alta y, a menudo, necesita más constantes) ( ver comentario ).
  6. El código ensamblador x86 es complicado porque x86 es una arquitectura complicada con muchas características. Una lista de instrucciones para una máquina MIPS típica cabe en una sola hoja de papel de tamaño carta. La lista equivalente para x86 llena varias páginas, y las instrucciones solo hacen más, por lo que a menudo necesita una explicación más amplia de lo que hacen que la que puede proporcionar una lista. Por ejemplo, la MOVSBinstrucción necesita un bloque relativamente grande de código C para describir lo que hace:

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

    Esa es una sola instrucción que realiza una carga, una tienda y dos sumas o restas (controladas por una entrada de bandera), cada una de las cuales serían instrucciones separadas en una máquina RISC.

    Si bien la simplicidad de MIPS (y arquitecturas similares) no los hace necesariamente superiores, para enseñar una introducción a la clase de ensamblador tiene sentido comenzar con una ISA más simple . Algunas clases de ensamblaje enseñan un subconjunto ultra-simplificado de x86 llamado y86 , que se simplifica más allá del punto de no ser útil para el uso real (por ejemplo, sin instrucciones de cambio), o algunas enseñan solo las instrucciones básicas de x86.

  7. El x86 usa códigos de operación de longitud variable, que agregan complejidad al hardware con respecto al análisis de instrucciones. En la era moderna, este costo se está volviendo cada vez más pequeño a medida que las CPU se vuelven cada vez más limitadas por el ancho de banda de la memoria que por la computación en bruto, pero muchos artículos y actitudes de "ataque x86" provienen de una era en la que este costo era comparativamente mucho mayor.
    Actualización 2016: Anandtech ha publicado una discusión sobre los tamaños de los códigos de operación en x64 y AArch64 .

EDITAR: ¡Esto no se supone que sea un golpe para el x86! partido. No tuve más remedio que hacer algunas críticas dada la forma en que está redactada la pregunta. Pero con la excepción de (1), todas estas cosas se hicieron por buenas razones (ver comentarios). Los diseñadores de Intel no son estúpidos, querían lograr algunas cosas con su arquitectura, y estos son algunos de los impuestos que tuvieron que pagar para hacer esas cosas una realidad.

Billy ONeal
fuente
17
Es una compensación. Es un punto fuerte que el tamaño binario puede ser más pequeño, pero es un punto débil que necesita un hardware muy complicado para implementar un analizador para estas instrucciones. La gran mayoría de las instrucciones son del mismo tamaño de todos modos; la mayor parte de la razón de los códigos de operación de longitud variable en x86 es cuando decidieron agregar funciones y descubrieron que no podían representar lo que querían en la cantidad de bits con los que tenían que trabajar. . A la gran mayoría de las personas no les preocupa tanto el tamaño binario como la complejidad del hardware o el consumo de energía.
Billy ONeal
8
@Joey Adams: contrasta las instrucciones de longitud variable de x86 con el modo de pulgar de ARM ( en.wikipedia.org/wiki/ARM_architecture#Thumb ). El modo de pulgar da como resultado un código de objeto significativamente más pequeño para el ARM porque las instrucciones más cortas se asignan directamente a las instrucciones normales. Pero como hay un mapeo 1: 1 entre las instrucciones más grandes y las más pequeñas, el hardware de análisis es fácil de implementar. Las instrucciones de longitud variable de x86 no tienen estos beneficios porque no fueron diseñadas de esa manera en primer lugar.
Billy ONeal
7
(6) No todos los códigos de operación necesitan ser usados ​​por todos los programas, pero maldita sea, cuando necesito SSE3, me alegro de tenerlo.
Chris K
4
@Chris Kaminski: ¿Cómo no afecta eso al hardware? Claro, en una computadora moderna de tamaño completo a nadie le va a importar, pero si estoy fabricando algo como un teléfono celular, me preocupa más el consumo de energía que casi cualquier otra cosa. Los códigos de operación de longitud variable no aumentan el tiempo de ejecución, pero el hardware de decodificación aún requiere energía para funcionar.
Billy ONeal
5
¿Cuál es una de las cosas que hacen que el conjunto de instrucciones x86 sea tan feo, ya que no puede decidir si es un acumulador o una arquitectura basada en archivos de registro (aunque esto se solucionó principalmente con el 386, lo que hizo que el conjunto de instrucciones fuera mucho más ortogonal , independientemente de lo que te digan los fanáticos de 68k).
ninjalj
25

El principal golpe contra x86 en mi mente son sus orígenes CISC: el conjunto de instrucciones contiene muchas interdependencias implícitas. Estas interdependencias hacen que sea difícil hacer cosas como el reordenamiento de instrucciones en el chip, porque los artefactos y la semántica de esas interdependencias deben conservarse para cada instrucción.

Por ejemplo, la mayoría de las instrucciones de suma y resta de enteros x86 modifican el registro de banderas. Después de realizar una suma o resta, la siguiente operación suele ser mirar el registro de banderas para comprobar si hay desbordamiento, bit de signo, etc. Si hay otra suma después de eso, es muy difícil saber si es seguro comenzar la ejecución de la segunda suma antes de que se conozca el resultado de la primera adición.

En una arquitectura RISC, la instrucción de adición especificaría los operandos de entrada y los registros de salida, y todo lo relacionado con la operación se llevaría a cabo utilizando solo esos registros. Esto hace que sea mucho más fácil desacoplar operaciones de adición que están cerca unas de otras porque no hay registros de banderas florecientes que obliguen a todo a alinearse y ejecutar un solo archivo.

El chip DEC Alpha AXP, un diseño RISC estilo MIPS, fue dolorosamente espartano en las instrucciones disponibles, pero el conjunto de instrucciones fue diseñado para evitar dependencias de registro implícitas entre instrucciones. No había ningún registro de pila definido por hardware. No había registro de banderas definidas por hardware. Incluso el puntero de instrucción estaba definido por el sistema operativo: si deseaba volver a la persona que llama, tenía que averiguar cómo la persona que llama le informaría a qué dirección regresar. Esto generalmente se define por la convención de llamadas del sistema operativo. En el x86, sin embargo, está definido por el hardware del chip.

De todos modos, a lo largo de 3 o 4 generaciones de diseños de chips Alpha AXP, el hardware pasó de ser una implementación literal del conjunto de instrucciones espartano con 32 registros int y 32 registros flotantes a un motor de ejecución masivamente fuera de orden con 80 registros internos, cambio de nombre de registros, reenvío de resultados (donde el resultado de una instrucción anterior se reenvía a una instrucción posterior que depende del valor) y todo tipo de potenciadores de rendimiento salvajes y locos. Y con todas esas campanas y silbidos, el chip AXP todavía era considerablemente más pequeño que el chip Pentium comparable de esa época, y el AXP era muchísimo más rápido.

No ve ese tipo de ráfagas de rendimiento que mejoran las cosas en el árbol genealógico x86 en gran parte porque la complejidad del conjunto de instrucciones x86 hace que muchos tipos de optimizaciones de ejecución sean prohibitivamente costosas, si no imposibles. El golpe de genio de Intel fue renunciar a la implementación del conjunto de instrucciones x86 en el hardware: todos los chips x86 modernos son en realidad núcleos RISC que, hasta cierto punto, interpretan las instrucciones x86, traduciéndolas en un microcódigo interno que conserva toda la semántica del x86 original. instrucción, pero permite un poco de ese RISC fuera de orden y otras optimizaciones sobre el microcódigo.

He escrito mucho ensamblador x86 y puedo apreciar plenamente la conveniencia de sus raíces CISC. Pero no me di cuenta de lo complicado que era x86 hasta que pasé un tiempo escribiendo el ensamblador Alpha AXP. Me quedé atónito por la simplicidad y uniformidad de AXP. Las diferencias son enormes y profundas.

dthorpe
fuente
6
No escucharé ataques a CISC per se a menos que y hasta que pueda explicar m68k.
dmckee --- ex-moderador gatito
2
No estoy familiarizado con el m68k, así que no puedo criticarlo.
dthorpe
4
No creo que esta respuesta sea lo suficientemente mala como para rechazarla, pero sí creo que todo el argumento de "RISC es más pequeño y más rápido que CISC" no es realmente relevante en la era moderna. Claro, el AXP podría haber sido mucho más rápido para su momento, pero el hecho es que los RISC modernos y los CISC modernos son casi lo mismo en lo que respecta al rendimiento. Como dije en mi respuesta, la pequeña penalización de energía para la decodificación x86 es una razón para no usar x86 para algo como un teléfono móvil, pero ese es un pequeño argumento para una computadora de escritorio o portátil de tamaño completo.
Billy ONeal
4
@Billy: el tamaño es más que el tamaño del código o el tamaño de la instrucción. Intel paga una gran penalización en el área de la superficie del chip para implementar la lógica del hardware para todas esas instrucciones especiales, con el núcleo de microcódigo RISC bajo el capó o no. El tamaño de la matriz afecta directamente el costo de fabricación, por lo que sigue siendo una preocupación válida con los diseños de sistemas modernos.
dthorpe
1
@dthorpe: No estoy de acuerdo con la mayoría, si no con todo, de lo que escribiste. Desde el 8086, no tenía que preocuparse si era seguro ejecutar uno addtras otro add. Las reglas son claras. Tampoco es necesario que se ocupe de la reordenación de instrucciones. Desde el Pentium Pro a mediados de los 90, la CPU lo hace por usted. Lo que está mencionando puede haber sido un problema hace 20 años, pero no veo ninguna razón para oponerse a la arquitectura x86 hoy en día.
Nathan Fellman
21

La arquitectura x86 data del diseño del microprocesador 8008 y sus parientes. Estas CPU fueron diseñadas en una época en la que la memoria era lenta y si podía hacerlo en la CPU, a menudo era mucho más rápido. Sin embargo, el espacio de la CPU también era caro. Estas dos razones explican por qué hay solo una pequeña cantidad de registros que tienden a tener propósitos especiales y un conjunto de instrucciones complicado con todo tipo de trampas y limitaciones.

Otros procesadores de la misma época (por ejemplo, la familia 6502) también tienen limitaciones y peculiaridades similares. Curiosamente, tanto la serie 8008 como la serie 6502 se diseñaron como controladores integrados. Incluso en ese entonces, se esperaba que los controladores embebidos estuvieran programados en ensamblador y, en muchos sentidos, se dirigieran al programador del ensamblador en lugar del escritor del compilador. (Mire el chip VAX para ver lo que sucede cuando se ocupa de la escritura del compilador). Los diseñadores no esperaban que se convirtieran en plataformas informáticas de propósito general; para eso estaban las cosas como los predecesores de la arquitectura POWER. La revolución del Home Computer cambió eso, por supuesto.

estático
fuente
4
+1 por la única respuesta aquí de alguien que realmente parece tener antecedentes históricos sobre el tema.
Billy ONeal
3
La memoria siempre ha sido lenta. Es posible que (en términos relativos) sea más lento hoy que cuando comencé con Z80s y CP / M en 1982. La extinción no es el único camino de evolución porque con la extinción esa dirección evolutiva particular se detiene. Yo diría que el x86 se ha adaptado bien en sus 28 años (hasta ahora de existencia).
Olof Forshell
4
Las velocidades de memoria alcanzaron brevemente casi la paridad con las CPU en la época del 8086. El 9900 de Texas Instruments tiene un diseño que solo funciona porque esto sucedió. Pero luego la CPU se adelantó nuevamente y se quedó allí. Solo ahora, hay cachés para ayudar a administrar esto.
staticsan
3
@Olof Forshell: era compatible con ensamblador, ya que el código ensamblador 8080 podía traducirse en código 8086. Desde ese punto de vista, eran 8080 más extensiones, al igual que si pudieras ver 8080 como 8008 más extensiones.
David Thornley
3
@Olof Forshell: Excepto que el 8086 fue diseñado para que eso suceda. Era una extensión del 8080, y la mayoría (posiblemente todas) las instrucciones del 8080 estaban asignadas una a una, con una semántica obviamente similar. Eso no es cierto en el caso de la arquitectura IBM 360, independientemente de la forma en que desee impulsarla.
David Thornley
13

Tengo algunos aspectos adicionales aquí:

Considere la operación "a = b / c" x86 implementaría esto como

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

Como una ventaja adicional de la instrucción div, edx contendrá el resto.

Un procesador RISC requeriría primero cargar las direcciones de byc, cargar byc de la memoria a los registros, hacer la división y cargar la dirección de ay luego almacenar el resultado. Sintaxis de dst, 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

Aquí normalmente no quedará un resto.

Si alguna variable se va a cargar a través de punteros, ambas secuencias pueden volverse más largas, aunque esto es una posibilidad menor para el RISC porque puede tener uno o más punteros ya cargados en otro registro. x86 tiene menos registros, por lo que la probabilidad de que el puntero esté en uno de ellos es menor.

Pros y contras:

Las instrucciones RISC se pueden mezclar con el código circundante para mejorar la programación de instrucciones, esto es menos posible con x86 que, en cambio, hace este trabajo (más o menos bien dependiendo de la secuencia) dentro de la propia CPU. La secuencia RISC anterior normalmente tendrá una longitud de 28 bytes (7 instrucciones de 32 bits / 4 bytes de ancho cada una) en una arquitectura de 32 bits. Esto hará que la memoria fuera del chip funcione más al recuperar las instrucciones (siete recuperaciones). La secuencia x86 más densa contiene menos instrucciones y, aunque sus anchos varían, probablemente también esté viendo un promedio de 4 bytes / instrucción allí. Incluso si tiene cachés de instrucciones para acelerar esto, siete recuperaciones significa que tendrá un déficit de tres en otros lugares para compensar en comparación con el x86.

La arquitectura x86 con menos registros para guardar / restaurar significa que probablemente hará cambios de hilo y manejará las interrupciones más rápido que RISC. Más registros para guardar y restaurar requieren más espacio de pila de RAM temporal para realizar interrupciones y más espacio de pila permanente para almacenar estados de subprocesos. Estos aspectos deberían hacer que x86 sea un mejor candidato para ejecutar RTOS puros.

En una nota más personal, me resulta más difícil escribir un ensamblaje RISC que x86. Resuelvo esto escribiendo la rutina RISC en C, compilando y modificando el código generado. Esto es más eficiente desde el punto de vista de la producción de código y probablemente menos eficiente desde el punto de vista de la ejecución. Todos esos 32 registros para realizar un seguimiento. Con x86 es al revés: 6-8 registros con nombres "reales" hacen que el problema sea más manejable e infunde más confianza en que el código producido funcionará como se esperaba.

¿Feo? Eso está en el ojo del espectador. Prefiero "diferente".

Olof Forshell
fuente
a, byc en mis ejemplos deben verse como variables basadas en memoria y no como valores inmediatos.
Olof Forshell
... "dword ptr" se utiliza para especificar el tamaño de una variable cuyo tamaño se desconoce si, por ejemplo, simplemente se declara como externa o si ha sido vago.
Olof Forshell
2
Esa no es la primera vez que escucho la sugerencia de escribirlo en C primero y luego destilarlo en ensamblador. Eso definitivamente ayuda
Joe Plante
En los primeros días, todos los procesadores eran RISC. CISC surgió como una estrategia de mitigación para los sistemas de memoria de núcleo férrico que eran MUY lentos, por lo que CISC, con menos instrucciones más potentes, puso menos estrés en el subsistema de memoria y aprovechó mejor el ancho de banda. Del mismo modo, los registros se pensaron originalmente como ubicaciones de memoria en el chip, en la CPU para realizar acumulaciones. La última vez que comparé seriamente una máquina RISC fue en 1993: SPARC y HP Prisim. SPARC fue horrible en todos los ámbitos. Prisim fue hasta 20 veces más rápido que un 486 en add / sub / mul, pero apestaba a lo trascendental. CISC es mejor.
@OlofForshell Dices there typically won't be a reminderpero wiki dice que los mips lo tienen: en.wikipedia.org/wiki/MIPS_instruction_set#Integer
Alex Zhukovskiy
10

Creo que esta pregunta tiene una suposición falsa. Son principalmente los académicos obsesionados con RISC los que llaman feo a x86. En realidad, el x86 ISA puede realizar en una sola instrucción operaciones que tomarían 5-6 instrucciones en RISC ISA. Los fanáticos de RISC pueden contrarrestar que las CPU x86 modernas dividen estas instrucciones "complejas" en microops; sin embargo:

  1. En muchos casos, eso es solo parcialmente cierto o no es cierto en absoluto. Las instrucciones "complejas" más útiles en x86 son cosas como, por mov %eax, 0x1c(%esp,%edi,4)ejemplo, modos de direccionamiento, y no están desglosadas.
  2. Lo que suele ser más importante en las máquinas modernas no es la cantidad de ciclos empleados (porque la mayoría de las tareas no están vinculadas a la CPU) sino el impacto del código en la caché de instrucciones. 5-6 instrucciones de tamaño fijo (generalmente de 32 bits) afectarán a la caché mucho más que una instrucción compleja que rara vez supera los 5 bytes.

x86 realmente absorbió todos los aspectos buenos de RISC hace unos 10-15 años, y las cualidades restantes de RISC (en realidad, la definitoria , el conjunto mínimo de instrucciones) son dañinas e indeseables.

Aparte del costo y la complejidad de la fabricación de las CPU y sus requisitos de energía, x86 es el mejor ISA . Cualquiera que le diga lo contrario está dejando que la ideología o la agenda se interponga en su razonamiento.

Por otro lado, si está apuntando a dispositivos integrados donde el costo de la CPU cuenta, o dispositivos integrados / móviles donde el consumo de energía es una preocupación principal, ARM o MIPS probablemente tengan más sentido. Sin embargo, tenga en cuenta que aún tendrá que lidiar con la memoria RAM adicional y el tamaño binario necesarios para manejar un código que es fácilmente 3-4 veces más grande, y no podrá acercarse al rendimiento. Si esto es importante, depende en gran medida de lo que esté ejecutando.

R .. GitHub DEJA DE AYUDAR A ICE
fuente
3
donde el consumo de energía es una preocupación principal, ARM o MIPS probablemente tengan más sentido ... entonces, si hay al menos un aspecto en el que ARM o MIPS tienen más sentido, ¿no hace que x86 no sea necesariamente el mejor ISA?
Shahbaz
Por eso califiqué "los mejores" con "además del costo ... y sus requerimientos energéticos".
R .. GitHub DEJA AYUDAR A ICE
1
Creo que Intel está reduciendo la velocidad de la CPU y los tamaños de troquel más pequeños han eliminado en gran medida el diferencial de potencia. La nueva CPU Celeron dual de 64 bits con cachés de 64k L1 y 1MB L2 es un chip de 7.5 vatios. Es mi máquina de reunión "Starbucks", y la duración de la batería es ridículamente larga y funcionará alrededor de una máquina P6. Como un tipo que hacía principalmente cálculos de punto flotante, renuncié a RISC hace mucho tiempo. Simplemente se arrastra. SPARC en particular fue atrozmente glacial. El ejemplo perfecto de por qué RISC apesta fue la CPU Intel i860. Intel nunca volvió a ir ALLÍ.
@RocketRoy: 7.5 vatios no es realmente aceptable para un dispositivo que funciona las 24 horas del día, los 7 días de la semana (y que no realiza cálculos útiles todo el tiempo) o que funciona con una batería de 3.7v / 2000mAh.
R .. GitHub DEJA DE AYUDAR A ICE
2
@RocketRoy "CPU Intel i860. Intel nunca más fue ALLÍ". Después de un poco de investigación, el i860 se parece mucho a Itanium: VLIW, paralelismo de instrucciones ordenadas por el compilador ...
Jonathon Reinhart
9

El lenguaje ensamblador x86 no es tan malo. Es cuando llegas al código de la máquina que comienza a ponerse realmente feo. Las codificaciones de instrucciones, los modos de direccionamiento, etc. son mucho más complicados que los de la mayoría de las CPU RISC. Y hay diversión adicional incorporada para propósitos de compatibilidad con versiones anteriores, cosas que solo se activan cuando el procesador está en cierto estado.

En los modos de 16 bits, por ejemplo, el direccionamiento puede parecer francamente extraño; hay un modo de direccionamiento para [BX+SI], pero no uno para [AX+BX]. Cosas como esa tienden a complicar el uso del registro, ya que debe asegurarse de que su valor esté en un registro que pueda usar cuando lo necesite.

(Afortunadamente, el modo de 32 bits es mucho más sensato (aunque a veces sigue siendo un poco extraño, por ejemplo, la segmentación), y el código x86 de 16 bits ya es en gran medida irrelevante fuera de los cargadores de arranque y algunos entornos integrados).

También están las sobras de los viejos tiempos, cuando Intel intentaba hacer de x86 el procesador definitivo. Instrucciones de un par de bytes de longitud que realizaban tareas que ya nadie hace, porque francamente eran demasiado lentas o complicadas. Las instrucciones ENTER y LOOP , para dos ejemplos - tenga en cuenta que el código del marco de pila C es como "push ebp; mov ebp, esp" y no "enter" para la mayoría de los compiladores.

cHao
fuente
2
Creo que el problema de "enter" versus "push / mov" surgió porque en algunos procesadores, "push / mov" es más rápido. En algunos procesadores, "enter" es más rápido. Así es la vida.
Dietrich Epp
4
Cuando me vi obligado a usar una máquina basada en x86 y comencé a echarle un vistazo (con antecedentes de m68k), comencé a sentirme frustrante en la programación, ... como si hubiera aprendido a programar con un lenguaje como C, y luego ser forzado a entrar en contacto con asm ... sientes que pierdes poder de expresión, facilidad, claridad, "coherencia", "intuición". Estoy seguro de que si hubiera comenzado a programar asm con x86, habría pensado no es tan malo ... quizás ... también hice MMIX y MIPS, y su "asm lang" es mucho mejor que x86 (si este es el punto de vista correcto para la Q, pero quizás no lo sea)
ShinTakezou
El problema del modo de direccionamiento se solucionó en el 80386. Sólo el código de 16 bits tiene modos de direccionamiento limitados, el código de 32 bits es mucho mejor. Puede obtener los modos de direccionamiento de 32 bits en código de 16 bits utilizando un prefijo especial y viceversa.
fuz
@FUZxxl: Sí ... probablemente debería haber mencionado que la fealdad se limita principalmente al código de 16 bits. Fijo (creo). :)
cHao
La falta de elegancia percibida proviene principalmente de la idea errónea de que los registros de un 8086 son registros de propósito general; eso es incorrecto. Cada uno de ellos tiene un propósito especial y si no te ciñes a sus propósitos, lo vas a pasar mal.
fuz
3

No soy un experto, pero parece que muchas de las características por las que a la gente no le gusta pueden ser las razones por las que funciona bien. Hace varios años, tener registros (en lugar de una pila), marcos de registro, etc. se consideraba una buena solución para hacer que la arquitectura pareciera más simple para los humanos. Sin embargo, hoy en día, lo que importa es el rendimiento de la caché, y las palabras de longitud variable de x86 le permiten almacenar más instrucciones en la caché. La "decodificación de instrucciones", que creo que los oponentes señalaron una vez tomó la mitad del chip, ya no es tanto así.

Creo que el paralelismo es uno de los factores más importantes hoy en día, al menos para los algoritmos que ya se ejecutan lo suficientemente rápido como para ser utilizables. Expresar un alto paralelismo en el software permite que el hardware amortice (o, a menudo, oculte por completo) las latencias de la memoria. Por supuesto, el futuro de la arquitectura de mayor alcance probablemente esté en algo como la computación cuántica.

Escuché de nVidia que uno de los errores de Intel fue que mantuvieron los formatos binarios cerca del hardware. El PTX de CUDA realiza algunos cálculos rápidos de uso de registros (coloración de gráficos), por lo que nVidia puede usar una máquina de registro en lugar de una máquina de pila, pero aún tiene una ruta de actualización que no rompe todo el software antiguo.

gatoatigrado
fuente
9
RISC no fue diseñado pensando en desarrolladores humanos. Una de las ideas detrás de RISC fue descargar algo de la complejidad del chip en quien escribió el ensamblaje, idealmente el compilador. Más registros significan menos uso de memoria y menos dependencias entre instrucciones, lo que permite canalizaciones más profundas y mayor rendimiento. Tenga en cuenta que x86-64 tiene el doble de registros generales que x86, y esto por sí solo es responsable de importantes ganancias de rendimiento. Y las instrucciones en la mayoría de los chips x86 se decodifican antes de que se almacenen en caché, no después (por lo que el tamaño no importa aquí).
Dietrich Epp
3
@Dietrich Epp: Eso no es del todo cierto. El x86-64 tiene más registros visibles en el ISA, pero las implementaciones x86 modernas generalmente tienen un archivo de registro de estilo RISC que se asigna a los registros del ISA a pedido para acelerar la ejecución.
Billy ONeal
"Escuché de nVidia que uno de los errores de Intel fue que mantuvieron los formatos binarios cerca del hardware". - No entendí esto y la parte PTX de CUDA.
garras
1
@Dietrech Epp: "Y las instrucciones en la mayoría de los chips x86 se decodifican antes de que se almacenen en caché, no después" Eso no es cierto. Se almacenan en caché antes de decodificarlos. Creo que el Pentium 4 tenía un caché de seguimiento adicional que se almacenaba en caché después de la decodificación, pero se ha descontinuado.
Nathan Fellman
eso no es cierto, los procesadores "sandy bridge" más nuevos usan una especie de caché de rastreo (como el del Pentium 4, oh, ese viejo: D), por lo que las tecnologías desaparecen y regresan ...
Quonux
3

Además de las razones por las que la gente ya ha mencionado:

  • x86-16 tenía un esquema de direccionamiento de memoria bastante extraño que permitía direccionar una única ubicación de memoria de hasta 4096 formas diferentes, RAM limitada a 1 MB y obligaba a los programadores a trabajar con dos tamaños diferentes de punteros. Afortunadamente, el cambio a 32 bits hizo que esta característica fuera innecesaria, pero los chips x86 aún llevan el grueso de los registros de segmento.
  • Si bien no es un fallo de 86 per se , convenciones de llamada x86 no se estandarizaron como MIPS era (en su mayoría porque MS-DOS no viene con ninguna compiladores), que nos deja con el lío de __cdecl, __stdcall, __fastcall, etc.
dan04
fuente
Hmm ... cuando pienso en competidores x86, no pienso en MIPS. ARM o PowerPC tal vez ...
Billy ONeal
@Billy: x86 ha existido desde siempre. Hubo un tiempo en que MIPS era un competidor x86. Según recuerdo, x86 tuvo que trabajar mucho para llegar a un nivel en el que fuera competitivo con MIPS. (Antes, cuando MIPS y SPARC estaban peleando en la arena de las estaciones de trabajo)
Shannon Severance
@Shannon Severance: El hecho de que algo fuera una vez no significa que lo sea.
Billy ONeal
2
@supercat: lo que la gente en la era del modelo plano de memoria x86-32 tiende a olvidar es que 16 bits significa 64k de memoria (cualquiera que se moleste en hacer los cálculos entenderá que la magia no es posible, que el 8086 no era un un castigo desagradable para los programadores desprevenidos). Hay algunas formas de conseguir 64k, pero la solución 8086 fue un buen compromiso.
Olof Forshell
2
@OlofForshell: Creo que mucha gente se lamentaba del hecho de que el 8086 no era tan bueno como el 68000 (que tenía un espacio de direccionamiento lineal de 16 MB y una ruta clara a 4 gigas). Ciertamente, ir a un procesador de 32 bits facilitará el acceso a más de 64K, pero el 8086 es una arquitectura de 16 bits que fue diseñada para ser un paso adelante del 8080 de 8 bits. No veo ninguna razón por la que Intel debería haber saltado directamente de uno de 8 bits a uno de 32 bits.
Supercat
3

Creo que llegará a parte de la respuesta si alguna vez intenta escribir un compilador que apunte a x86, o si escribe un emulador de máquina x86, o incluso si intenta implementar la ISA en un diseño de hardware.

Aunque entiendo que "¡x86 es feo!" argumentos, sigo pensando que es más divertido escribir ensamblaje x86 que MIPS (por ejemplo); este último es simplemente tedioso. Siempre tuvo la intención de ser más agradable para los compiladores que para los humanos. No estoy seguro de que un chip pueda ser más hostil para los escritores de compiladores si lo intentara ...

La parte más fea para mí es la forma en que funciona la segmentación (en modo real): que cualquier dirección física tiene un segmento 4096: alias de compensación. ¿Cuándo fue la última vez que lo necesitó ? Las cosas habrían sido mucho más sencillas si la parte del segmento fuera estrictamente bits de orden superior de una dirección de 32 bits.

Bernd Jendrissek
fuente
m68k es mucho más divertido y agradable para los humanos mucho más que x86 (que no puede parecer tan "humano" a muchos programadores de m68k), si el PoV correcto es la forma en que los humanos pueden escribir código en ese ensamblaje.
ShinTakezou
El segmento: direccionamiento de compensación fue un intento de mantener la compatibilidad hasta cierto punto con el mundo CP / M. Una de las peores decisiones de la historia.
Turing completo
@Turing Complete: segmento: desplazamiento NO fue principalmente un intento de mantener la compatibilidad con el mundo CP / M. Lo que fue un intento muy exitoso de permitir que un procesador de 16 bits direccionara más de 64 KBytes colocando código, datos, pila y otras áreas de memoria en diferentes segmentos.
Olof Forshell
1
En realidad, colocar datos y pilas en diferentes segmentos fue completamente inútil para C; solo se podía usar para asm. En C, un puntero puede apuntar a datos con una duración de almacenamiento estática, automática o asignada dinámicamente, por lo que no hay forma de eludir el segmento. Tal vez fue útil para Pascal o Fortran o algo así, pero no para C, que ya era el idioma dominante en ese momento ...
R .. GitHub DEJA AYUDAR A ICE
2
@Bernd: La razón por la que se eligieron fs / gs para el almacenamiento local de subprocesos no es que los registros de segmento sean buenos para esto. Es solo que x86 está seriamente hambriento de registros y los registros de segmento no se usaron. Un registro de propósito general apuntando a la estructura del hilo habría funcionado igual de bien y, de hecho, muchos sistemas RISC con más registros utilizan uno como puntero del hilo.
R .. GitHub DEJA DE AYUDAR A ICE
1
  1. x86 tiene un conjunto muy, muy limitado de registros de propósito general

  2. promueve un estilo de desarrollo muy ineficiente en el nivel más bajo (infierno CISC) en lugar de una metodología eficiente de carga / almacenamiento

  3. Intel tomó la espantosa decisión de introducir el modelo de dirección de memoria de segmento / desplazamiento claramente estúpido para mantenerse compatible con (¡en este momento ya!) Tecnología obsoleta

  4. En un momento en el que todos iban a 32 bits, el x86 frenó el mundo de las PC convencionales al ser un exiguo de 16 bits (la mayoría de ellos, el 8088, incluso solo con rutas de datos externas de 8 bits, ¡lo que es aún más aterrador!)


Para mí (¡y soy un veterano de DOS que ha visto todas y cada una de las generaciones de PC desde la perspectiva de los desarrolladores!), El punto 3. fue el peor.

Imagina la siguiente situación que tuvimos a principios de los 90 (¡corriente principal!):

a) Un sistema operativo que tenía limitaciones locas por razones heredadas (640kB de RAM de fácil acceso) - DOS

b) Una extensión del sistema operativo (Windows) que podía hacer más en términos de RAM, pero estaba limitada cuando se trataba de cosas como juegos, etc. y no era lo más estable en la Tierra (afortunadamente, esto cambió más tarde, pero yo estoy hablando de principios de los 90 aquí)

c) La mayoría del software seguía siendo DOS y teníamos que crear discos de arranque a menudo para software especial, porque había este EMM386.exe que a algunos programas les gustaba, otros odiaban (especialmente a los jugadores, y yo era un jugador de AVID en este momento, sé lo que estoy hablando de aquí)

d) Estábamos limitados a MCGA 320x200x8 bits (ok, había un poco más con trucos especiales, 360x480x8 era posible, pero solo sin soporte de biblioteca en tiempo de ejecución), todo lo demás era desordenado y horrible ("VESA" - lol)

e) Pero en términos de hardware, teníamos máquinas de 32 bits con bastantes megabytes de RAM y tarjetas VGA con soporte de hasta 1024x768

¿Razón de esta mala situación?

Una simple decisión de diseño de Intel. Nivel de instrucción de la máquina (¡NO nivel binario!) Compatibilidad con algo que ya estaba muriendo, creo que era el 8085. Los otros problemas aparentemente no relacionados (modos gráficos, etc.) estaban relacionados por razones técnicas y debido a la muy estrecha arquitectura mental que la plataforma x86 trajo consigo.

Hoy en día, la situación es diferente, pero pregúntele a cualquier desarrollador de ensambladores o personas que construyan backends de compilador para x86. El número increíblemente bajo de registros de propósito general no es más que un terrible asesino de rendimiento.

Turing completo
fuente
El único problema importante con la arquitectura segmentada del 8086 era que solo había un registro de segmento no dedicado (ES) y que los lenguajes de programación no estaban diseñados para funcionar con él de manera efectiva. El estilo de direccionamiento escalado que utiliza funcionaría muy bien en un lenguaje orientado a objetos que no espera que los objetos puedan comenzar en direcciones arbitrarias (si uno alinea objetos en los límites de los párrafos, las referencias a objetos solo necesitarán ser de dos bytes en lugar de cuatro). Si se compara el código anterior de Macintosh con el código de PC, el 8086 se ve bastante bien en comparación con el 68000.
supercat
@supercat: en realidad, el registro es estaba dedicado a algo, es decir, a esas instrucciones de cadena que requerían almacenamiento (movs, stos) o escaneo (cmps y scas). Dado el direccionamiento de 64 KB de cada registro de segmento, los es también proporcionaron el "eslabón perdido" a la memoria que no sea el código, los datos y la memoria de pila (cs, ds, ss). Los registros de segmento proporcionaban una especie de esquema de protección de la memoria en el que no se podía direccionar fuera de los bloques de memoria de 64 KB de los registros. ¿Qué mejor solución proponen dado que el x86 era una arquitectura de 16 bits y las limitaciones de la litografía del día?
Olof Forshell
@OlofForshell: ES se usó para instrucciones de cadena, pero podría usarse como un registro no comprometido para código que no las usa. Una forma de aliviar el cuello de botella seg-reg sin requerir demasiado espacio de código de operación sería tener un prefijo "rseg" que especificaría que para la siguiente instrucción de formato r / m, el campo "r" seleccionaría de CS / SS / DS / ES / FS / GS / ?? / ?? en lugar de AX / BX / CX / DX / SI / DI / SP / BP, y tener prefijos para FS / GS e instrucciones para LFS y LGS (como LDS y LES). No sé cómo se diseñó la microarquitectura del 8086, pero creo que algo así podría haber funcionado.
supercat
@supercat: como escribí, "los registros también proporcionan el enlace que falta a la memoria que no sea ..." Fs y gs no llegaron hasta el 386, según recuerdo.
Olof Forshell
1
@OlofForshell: No lo hicieron, lo que hizo que la arquitectura 80286 fuera incluso peor que la arquitectura 8086 en la mayoría de los aspectos. Mi punto fue que agregar un par de registros de segmento más (o incluso uno, para el caso) habría hecho que la arquitectura 8086 fuera mucho más útil, y el conjunto de instrucciones podría haber sido más limpio y más útil si se pudiera acceder a los registros de segmento de manera muy similar a la otros.
supercat