Me gustaría hacer algunas preguntas sobre el lenguaje ensamblador. Tengo entendido que está muy cerca del lenguaje de máquina, lo que lo hace más rápido y más eficiente.
Dado que tenemos diferentes arquitecturas de computadora, ¿eso significa que tengo que escribir un código diferente en Assembly para diferentes arquitecturas? Si es así, ¿por qué no es ensamblado, escribir una vez? ¿No sería más fácil simplemente hacerlo universal, para que lo escriba solo una vez y pueda ejecutarlo en prácticamente cualquier máquina con diferentes configuraciones? (Creo que sería imposible, pero me gustaría tener algunas respuestas concretas y profundas)
Algunas personas podrían decir que C es el lenguaje que estoy buscando. No he usado C antes, pero creo que sigue siendo un lenguaje de alto nivel, aunque probablemente más rápido que Java, por ejemplo. Podría estar equivocado aquí.
Respuestas:
El lenguaje ensamblador es una forma de escribir instrucciones para el conjunto de instrucciones de la computadora , de una manera que es un poco más comprensible para los programadores humanos.
Las diferentes arquitecturas tienen diferentes conjuntos de instrucciones: el conjunto de instrucciones permitidas es diferente en cada arquitectura. Por lo tanto, no puede esperar tener un programa de ensamblaje de escribir una vez y ejecutar en todas partes. Por ejemplo, el conjunto de instrucciones admitidas por los procesadores x86 se ve muy diferente del conjunto de instrucciones admitidas por los procesadores ARM. Si escribió un programa de ensamblaje para un procesador x86, tendría muchas instrucciones que no son compatibles con el procesador ARM, y viceversa.
La razón principal para usar el lenguaje ensamblador es que permite un control de muy bajo nivel sobre su programa y para aprovechar todas las instrucciones del procesador: al personalizar el programa para aprovechar las características que son exclusivas del procesador en particular. se ejecutará, a veces puedes acelerar el programa. La filosofía de escribir una vez y correr en todas partes está fundamentalmente en desacuerdo con eso.
fuente
La DEFINICIÓN del lenguaje ensamblador es que es un lenguaje que se puede traducir directamente al código de máquina. Cada código de operación en lenguaje ensamblador se traduce exactamente en una operación en la computadora de destino. (Bueno, es un poco más complicado que eso: algunos ensambladores determinan automáticamente un "modo de direccionamiento" basado en los argumentos de un código operativo. Pero aún así, el principio es que una línea de ensamblaje se traduce en una instrucción en lenguaje de máquina).
Sin duda, podría inventar un lenguaje que se parezca al lenguaje ensamblador, pero que se traduciría a diferentes códigos de máquina en diferentes computadoras. Pero por definición, eso no sería lenguaje ensamblador. Sería un lenguaje de nivel superior que se asemeja al lenguaje ensamblador.
Su pregunta es un poco como preguntar: "¿Es posible hacer un bote que no flote o tenga otra forma de viajar a través del agua, pero que tenga ruedas y un motor y pueda viajar por tierra?" La respuesta sería que, por definición, dicho vehículo no sería un barco. Suena más como un auto.
fuente
No hay ninguna conceptual (me atrevería a decir, ningún ordenador ciencia ) razón en contra de tener un lenguaje ensamblador para todos los equipos del mundo. De hecho, eso facilitaría muchas cosas. En lo que respecta a la teoría, todos son iguales, de todos modos, hasta una biyección funky.
En la práctica, sin embargo, hay diferentes chips para diferentes propósitos, con diferentes operaciones y principios de diseño (por ejemplo, RISC vs CISC) que sirven para diferentes objetivos, y los conjuntos de instrucciones que los operan y, por lo tanto, los lenguajes de ensamblaje difieren. Al final, la respuesta es la misma que cuando se pregunta por qué hay tantos lenguajes de programación diferentes: objetivos diferentes, decisiones de diseño diferentes.
Dicho esto, por supuesto, puede introducir niveles de abstracción para llegar a alguna interfaz compartida. x86, por ejemplo, se ha eliminado en el nivel de chip durante bastante tiempo; hay una pequeña pieza de hardware que traduce las instrucciones x86 a lo que su procesador realmente funcione. Lenguajes como C estarían un paso más allá del hardware (aunque sea uno muy pequeño), hasta lenguajes como Haskell, Java o Ruby. Sí, los compiladores son uno de los principales logros de la informática porque permiten separar las preocupaciones de esta manera.
fuente
Usted menciona la frase "escribir una vez que se ejecuta en cualquier lugar" sin que parezca darse cuenta de su importancia. Ese es el lema de marketing de Sun Microsystems que inventó comercialmente el concepto de una "máquina virtual" y "bytecodes" para Java, aunque posiblemente la idea puede tener su origen en el mundo académico 1 st. La idea fue copiada más tarde por Microsoft para .Net después de que Sun los demandó con éxito por incumplimiento de la licencia de Java. Los bytecodes de Java son una implementación de la idea de ensamblaje entre máquinas o lenguaje de máquina. Se usan para varios otros idiomas además de Java y, en teoría, se pueden usar para compilar cualquier lenguaje. Después de muchos años de optimización muy avanzada, Java se acerca en rendimiento a los lenguajes compilados que muestran que el objetivo de la tecnología de máquina virtual independiente de la plataforma de alto rendimiento es alcanzable en general.
Otra nueva idea en las primeras etapas / circulación relacionada con sus requisitos se llama proyecto de recálculo y es para investigación científica, aunque podría usarse para otros fines. La idea es hacer que los experimentos computacionales sean replicables a través de la tecnología de máquina virtual. Esta es principalmente la idea de simular diferentes arquitecturas de máquinas en hardware arbitrario.
fuente
Razones de alto nivel
Cuando lo piensa, un microprocesador hace una cosa asombrosa: le permite tomar una máquina (como una lavadora o un elevador) y reemplazar una gran cantidad de mecanismos o circuitos diseñados a medida con un silicio barato y producido en masa. chip. Se ahorra mucho dinero en piezas y mucho tiempo en diseño.
¿Pero espera un chip estándar que reemplaza innumerables diseños personalizados ? No puede haber un solo microprocesador perfecto que sea perfecto para cada aplicación. Algunas aplicaciones necesitan minimizar el uso de energía pero no necesitan ser rápidas; otros deben ser rápidos pero no deben ser fáciles de programar, otros deben ser de bajo costo, etc.
Por lo tanto, tenemos muchos "sabores" diferentes de microprocesador, cada uno con sus propias fortalezas y debilidades. Es deseable que todos usen un conjunto de instrucciones compatible, ya que esto permite la reutilización del código y facilita la búsqueda de personas con las habilidades adecuadas. Sin embargo, el conjunto de instrucciones hace afectar el costo, la complejidad, la velocidad, facilidad de uso y las limitaciones físicas del procesador, y por eso tenemos un compromiso: hay unos "principales" conjuntos de instrucciones (y muchos otros menores), y Dentro de cada conjunto de instrucciones hay muchos procesadores con diferentes características.
Ah, y a medida que la tecnología cambia, todas estas compensaciones cambian, por lo que los conjuntos de instrucciones evolucionan, surgen otros nuevos y los viejos mueren. Incluso si hubiera un "mejor" conjunto de instrucciones de hoy, podría no serlo en 20 años.
Detalles de hardware
Probablemente, la decisión de diseño más importante en un conjunto de instrucciones es el tamaño de la palabra , es decir, qué tan grande puede manipular el procesador "naturalmente". Los procesadores de 8 bits manejan números del 0 al 255, mientras que los procesadores de 32 bits manejan números del 0 al 4,294,967,295. El código diseñado para uno necesita ser completamente repensado para otro.
No se trata solo de traducir las instrucciones de un conjunto de instrucciones a otro. Un enfoque completamente diferente puede ser preferible en un conjunto de instrucciones diferente. Por ejemplo, en un procesador de 8 bits, una tabla de búsqueda puede ser ideal, mientras que en un procesador de 32 bits una operación aritmética sería mejor para el mismo propósito.
Existen otras diferencias importantes entre los conjuntos de instrucciones. La mayoría de las instrucciones se dividen en cuatro categorías:
Los procesadores difieren en el tipo de cálculos que pueden realizar, así como en la forma en que abordan el flujo de control, la transferencia de datos y la configuración del procesador.
Por ejemplo, algunos procesadores AVR no pueden multiplicarse ni dividirse; mientras que todos los procesadores x86 pueden. Como puede imaginar, eliminar los circuitos necesarios para tareas como la multiplicación y la división puede hacer que un procesador sea más simple y económico; estas operaciones aún pueden realizarse utilizando rutinas de software si son necesarias.
x86 permite instrucciones aritméticas para cargar sus operandos de la memoria y / o guardar sus resultados en la memoria; ARM es una arquitectura de almacén de carga y, por lo tanto, solo tiene unas pocas instrucciones dedicadas para acceder a la memoria. Mientras tanto, x86 tiene instrucciones dedicadas de ramificación condicional, mientras que ARM permite que prácticamente todas las instrucciones se ejecuten condicionalmente. Además, ARM permite realizar cambios de bits como parte de la mayoría de las instrucciones aritméticas. Estas diferencias conducen a diferentes características de rendimiento, diferencias en el diseño interno y el costo de los chips, y diferencias en las técnicas de programación a nivel de lenguaje ensamblador.
Conclusión
La razón por la que es imposible tener un lenguaje ensamblador universal es que, para convertir correctamente el código ensamblador de un conjunto de instrucciones a otro, uno debe diseñar el código nuevamente, algo que las computadoras aún no pueden hacer.
fuente
Agregando a la maravillosa respuesta de DW: si desea tener un ensamblador, necesitaría mantener todas las arquitecturas, un traductor perfecto entre ellas y comprender completamente lo que está haciendo.
Algunos códigos muy optimizados por arquitectura necesitarían ser desoptimizados, entendidos en un nivel más abstracto y optimizados por otro.
Pero si esto fuera posible, tendríamos un compilador C perfecto, y escribir en ensamblado puro no sería beneficioso en absoluto.
El punto principal de usar el ensamblador es el rendimiento, que no se puede extraer de los compiladores recientes.
Escribir dicho programa sería aún más difícil que los compiladores existentes y mantener todas las arquitecturas nuevas que se están creando lo haría aún más difícil.
Y para el programa "one only", también significaría compatibilidad total con versiones anteriores.
fuente
Microsoft inventó MSIL para ser un lenguaje ensamblador intermedio. Los programas se compilarían de C # o VB.Net a MSIL. En tiempo de ejecución, el MSIL se compiló en código de máquina para la máquina que lo estaba ejecutando utilizando un compilador JIT . El archivo que contenía el MSIL era un archivo .EXE con algunas instrucciones al principio en X86 para iniciar el programa. En un procesador ARM, escribiría la palabra mono delante del nombre del programa para ejecutarlo.
fuente
Como se señaló, LLVM es lo más cercano a esto hasta ahora. Una gran barrera para un lenguaje realmente universal serán las diferencias fundamentales relacionadas con las compensaciones implícitas: concurrencia, uso de memoria, rendimiento, latencia y consumo de energía. Si escribe en un estilo SIMD explícito, podría estar usando demasiada memoria. Si escribe en un estilo explícitamente SISD, obtendría una paralelización subóptima. Si optimiza el rendimiento, daña la latencia. Si maximiza el rendimiento de un solo subproceso (es decir, la velocidad del reloj), daña la vida útil de la batería.
Por lo menos, el código necesitaría ser anotado con las compensaciones. Lo que puede ser más importante es que el lenguaje tenga buenas propiedades algebraicas / tipo que le den al compilador mucho margen de maniobra para optimizar y detectar inconsistencias lógicas.
Luego está la cuestión del comportamiento indefinido. Gran parte de la velocidad de C y los lenguajes de ensamblaje provienen de un comportamiento indefinido. Si admite un comportamiento indefinido que realmente sucede, entonces termina manejándolos como casos especiales (es decir: pirateos específicos de arquitectura y contexto).
fuente
Quizás lo que está buscando es una notación de Universal Turning Machine donde todos estén de acuerdo con los símbolos de los comandos. ( https://en.wikipedia.org/wiki/Universal_Turing_machine )
Un 'ensamblador' que traduce un lenguaje Aceptable de Turning al código de máquina específico del proveedor subyacente y se construye para cualquiera de esas cosas que llamamos computadoras.
En The Art of Computer Programming hay un ejemplo de cómo se vería esto.
Pero considere la pregunta "por qué no es un lenguaje universal disponible en el mercado que se puede usar con todas las computadoras". Sugeriría que las influencias más dominantes son (1) conveniencia, no todos los lenguajes ensambladores son los más convenientes para usar; (2) la economía, el suministro, la incompatibilidad entre máquinas de diferentes marcas y proveedores es una estrategia comercial, así como el resultado de recursos limitados (tiempo / dinero) para diseñar máquinas.
fuente
supuesto: compilar y optimizar un lenguaje de alto nivel L1 a un lenguaje de nivel inferior L0 es más fácil que compilar y optimizar un lenguaje de alto nivel L2 (superior a L1) a L0; más fácil en el sentido de que supuestamente puede generar código más optimizado al compilar L1 a L0 que L2 a L0.
Creo que la suposición es probablemente correcta, por eso probablemente la mayoría de los compiladores usan un lenguaje intermedio de bajo nivel (IR / LLVM).
si esto es cierto, utilice cualquier lenguaje de bajo nivel L0 y escriba compiladores para traducir L0 a otros idiomas de bajo nivel. Por ejemplo, use el conjunto de instrucciones MIPS y compílelo a x86, arm, power, ...
-Taoufik
fuente