Recientemente, he estado leyendo algunos archivos SO y encontré declaraciones en contra de la arquitectura x86.
¿Por qué necesitamos una arquitectura de CPU diferente para servidor y mini / mainframe y núcleo mixto? dice
" La arquitectura de la PC es un desastre, cualquier desarrollador de SO te lo diría " .¿Vale la pena aprender el lenguaje ensamblador?( archivado ) dice
" Date cuenta de que la arquitectura x86 es horrible en el mejor de los casos "¿Alguna forma fácil de aprender ensamblador x86? dice
" La mayoría de las universidades enseñan ensamblado en algo como MIPS porque es mucho más simple de entender, el ensamblaje x86 es realmente feo "
y muchos más comentarios como
"En comparación con la mayoría de las arquitecturas, X86 apesta bastante".
" Definitivamente es la sabiduría convencional que X86 es inferior a MIPS, SPARC y PowerPC "
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?
Respuestas:
Un par de posibles razones para ello:
IN
yOUT
)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
MOVSB
instrucción necesita un bloque relativamente grande de código C para describir lo que hace: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.
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.
fuente
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.
fuente
add
tras otroadd
. 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.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.
fuente
Tengo algunos aspectos adicionales aquí:
Considere la operación "a = b / c" x86 implementaría esto como
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:
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".
fuente
there typically won't be a reminder
pero wiki dice que los mips lo tienen: en.wikipedia.org/wiki/MIPS_instruction_set#IntegerCreo 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:
mov %eax, 0x1c(%esp,%edi,4)
ejemplo, modos de direccionamiento, y no están desglosadas.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.
fuente
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.
fuente
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.
fuente
Además de las razones por las que la gente ya ha mencionado:
__cdecl
,__stdcall
,__fastcall
, etc.fuente
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.
fuente
x86 tiene un conjunto muy, muy limitado de registros de propósito general
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
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
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.
fuente