En lugar de compilar el código fuente para el sistema operativo respectivo (en el que está dirigido), compila una vez y se ejecuta en todas partes.
En aras de esta pregunta, lo llamaría VM (por ejemplo, tanto para Java como para .NET). Entonces, la ejecución de programas se convierte en algo así
------------ ---- ----
| Executable | -> | VM | -> | OS |
------------ ---- ----
Tiene mucho sentido, el compilador sigue siendo genérico para la VM respectiva. Sin embargo, la implementación de VM puede variar según la máquina en la que se va a instalar, es decir (* nix, windows, mac) x (32 bits, 64 bits).
Mi pregunta es, en lugar de escribir VM para las máquinas respectivas, ¿por qué el compilador no está escrito para esa máquina específica? Con esto, en lugar de descargar la VM respectiva, descarga el compilador respectivo y ese compilador se encargará del código de máquina + SO para esa máquina específica. Resultado final, la ejecución de código nativo para cualquier máquina. Definitivamente, cada código fuente necesitaría compilación para esa máquina específica, pero hoy en día, los sistemas automatizados, las compilaciones scm pueden ayudarnos a hacer esto.
¿Mis razones para estar confundido son correctas o me faltan algunos detalles técnicos aquí?
Editar:
PORTABILIDAD :
Sí, es una de las razones, pero ¿es la portabilidad un gran problema en los sistemas automatizados de hoy? ¿con qué frecuencia tenemos que preocuparnos por el hecho de que no tenemos que compilarlo para otras máquinas? Tener un código compilado para una máquina nativa daría un rendimiento mucho mejor. Tomemos Java, por ejemplo, no puede hacer programación de bajo nivel en Windows y debe elegir JNI.
Tome sistemas automatizados como TeamCity / Jenkins u otros. Podríamos tener una configuración de sistema tan automatizada donde el código enviado a través del control de versión resultaría en el ejecutable.
Respuestas:
Porque entonces ya no tendrías un ejecutable portátil; tendrías un ejecutable para cada plataforma. El usuario tendría que descargar un ejecutable específico para su plataforma, o compilar el código, en su plataforma específica.
Con una VM, cada plataforma solo necesita su VM específica de plataforma, y luego puede distribuir el mismo ejecutable a cada plataforma.
fuente
Te estás perdiendo algo enorme con estas máquinas virtuales. Hacen exactamente lo que dices, pero automáticamente. Se llama un compilador Just-In-Time y es por eso que .NET (en Windows) y Java están extremadamente cerca de la velocidad del código C ++ compilado de forma nativa.
El código fuente de Java / C # se convierte en código de bytes. Este código de bytes se compila en el código de máquina en la máquina en la que se está ejecutando actualmente. En su mayor parte, la VM ejecutará el código nativo en lugar de reprocesar el código de bytes.
Me he saltado un poco y simplifiqué demasiado el proceso, pero las máquinas virtuales hacen una gran cantidad de trabajo.
fuente
Program -> Compiled to byte code -> JIT for machine -> Execution
. Hay 4 pasos involucrados. Me pregunto por qué estamos usando estos 4 pasos. entonces podemosProgram -> Machine specific compiler -> Execute
Como muchos conceptos en informática, la VM proporciona una capa de abstracción. Usted escribe su código contra una 'interfaz' (código de bytes / lenguaje intermedio) y la capa de abstracción (VM) se ocupa de los detalles de implementación (compilándolo para la máquina de destino y otras tareas). La capa de abstracción le proporciona un conjunto de los servicios cuyos detalles de implementación ya no necesita preocuparse. En este caso, ya no necesita preocuparse por los detalles del hardware subyacente, dado que los servicios de capa de abstracción (VM) están presentes. El cliente se beneficia de manera similar: no necesita conocer los detalles sobre su plataforma, solo que puede usar la capa de abstracción.
Por supuesto, hay compensaciones con cualquier abstracción. Pierde el control detallado sobre los detalles en el otro lado de la abstracción, y tiene que confiar en esa abstracción para emplear una implementación sensata. Debe considerar las posibles ganancias mediante el uso de una abstracción contra las compensaciones. En ciertas aplicaciones, los inconvenientes pueden sopesar los beneficios: es posible que necesite ese alto nivel de control para todas y cada una de las plataformas.
Su propuesta de utilizar un sistema automatizado para compilaciones en diferentes plataformas es exactamente lo que ya hacen las capas de abstracción .NET y Java. Uno de los beneficios de la compilación JIT es que el compilador tiene detalles específicos sobre la máquina en la que se está ejecutando. Esto puede introducir potencial para optimizaciones que de otro modo no serían posibles al realizar una compilación de lanzamiento en la máquina de un desarrollador.
Si le preocupa la ralentización del tiempo de ejecución debido a que la generación de código nativo y la ejecución del programa ocurren juntas, tiene la opción de generar el código nativo durante la instalación en lugar de cuando el programa se ejecuta por primera vez. .NET proporciona nGen para este propósito, que puede realizar la generación de código nativo en el momento de la instalación en lugar del tiempo de ejecución. El CLR luego usará el código nativo en caché en lugar de realizar la compilación JIT.
fuente
Estás simplificando demasiado esto, multiplataforma es más que solo compilar para un conjunto específico de instrucciones nativas. Más común que no, el mismo código C / C ++ no se puede volver a compilar en diferentes plataformas porque las API y las bibliotecas son diferentes. Por ejemplo, el desarrollo de GUI es muy diferente en Windows y Ubuntu Linux, la comunicación de socket probablemente no sea idéntica en Unix e IBM z / OS, y así sucesivamente.
Por lo tanto, para lograr su "escribir una vez, compilar (y vincular) en cualquier lugar", tendría que crear una capa de abstracción de algún tipo, donde cree una API común para todos los servicios a nivel de sistema operativo. Luego debe implementar y distribuir esto para todas las plataformas que desea admitir.
Además, si bien los modelos de memoria son cada vez más similares hoy en día, todavía existen algunas diferencias entre las diferentes plataformas, por lo que también debe abstraer la asignación de memoria. Lo más fácil aquí es tal vez crear su propio administrador de memoria "virtual" además del nativo.
Mientras lo hace, los sistemas de archivos y el control de acceso (ACL) se manejan de manera bastante diferente entre, por ejemplo, Unix y Windows, por lo que también deberá abstraer esas cosas. Quizás incluso cree su propio contenedor "File" para implementaciones subyacentes.
Entonces tenemos hilos. Dado que Linux solo tiene procesos y Windows tiene subprocesos, también debemos abstraer esto de alguna manera.
Bueno, en este punto ya has construido una máquina virtual. El punto aquí es que si bien compilar un código arbitrario para diferentes plataformas es bastante fácil, construir software de trabajo de cualquier complejidad que pueda ejecutarse en cualquier plataforma está lejos de ser trivial.
fuente
Otra ventaja importante del enfoque VM / JIT es la compatibilidad binaria. Por ejemplo, supongamos que tiene un ensamblado / JAR / DLL que contiene la clase A y otro ensamblaje que contiene la clase B, que deriva de la clase A. ¿Qué sucede si cambia la clase A, por ejemplo, si agrega un miembro privado? Agregar un miembro privado no debería tener ninguna influencia en la clase B, pero si el ensamblado que contiene B ya se compiló en código nativo, probablemente obtendría errores extraños y bloqueos difíciles de reproducir, porque el código nativo se compiló con el diseño de memoria anterior de A, por lo que, por ejemplo, reservaría muy poca memoria para las variables de A, de modo que los miembros de A y los miembros B añadidos se asignarían a las mismas ubicaciones de memoria.
Por otro lado, si está utilizando una VM, todos estos problemas simplemente desaparecen, porque cuando el código se compila en código nativo, se conoce el diseño de todas las clases.
fuente
Imaginemos por un segundo que así es como funciona. Escribo una aplicación Java, que luego compilo; el compilador genera un ejecutable para cada plataforma compatible. Ahora tengo los siguientes ejecutables:
etcétera etcétera.
Luego subo todo esto a mi sitio web y proporciono un enlace de descarga para cada uno. Luego envío todo esto a download.com, tucows.com, sofpedia.com, enviando cada uno individualmente.
¡Esto me parece un gran dolor! Cuando actualizo el software a la versión 1.1, necesito repetir este proceso.
¿Y qué sucede cuando surge una nueva plataforma? Ahora mi software no está disponible para esta plataforma, a menos que actualice mi software de desarrollador para admitirlo, vuelva a compilar y luego vuelva a lanzar mi software para esta nueva plataforma.
¿Y qué hay del usuario? Visitan mi sitio y luego tienen que seleccionar de una lista exactamente la versión correcta de mi software para usar en su plataforma. La mayoría de los usuarios no saben si ejecutan x86 o x64, por lo que probablemente terminarán descargando la versión incorrecta y recibiendo algún tipo de error. ¿Qué pasa si luego cambian de Windows a una Mac, ahora necesitan volver y volver a descargar mi software?
Todo esto es un gran problema, tanto para el desarrollador como para el usuario, y todo esto se puede evitar simplemente compilando en un código de bytes y luego ejecutando a través de una VM, exactamente como lo hace Java actualmente.
Si el rendimiento es casi idéntico a la ejecución de código nativo, la pregunta no es tanto por qué no compilar en código nativo, sino por qué molestarse en compilar en código nativo.
fuente
En la edición de su publicación, cuestiona la utilidad de la portabilidad, wrt VM.
En mi vida profesional, la portabilidad ha sido el factor más importante: mi plataforma de implementación rara vez es la misma plataforma en la que implemento, por lo que puedo desarrollar y probar en Windows, pasar a mi departamento de control de calidad, que probaría en Linux e implementar en nuestro entorno de producción en Solaris.
Este no es solo un ejemplo aislado y artificial, sino que ha sido la base de mi existencia profesional durante los últimos 12 años de mi carrera de varias décadas. Estoy seguro de que hay muchos otros con la misma experiencia o experiencias similares.
Si utilizamos diferentes compiladores (cruzados) (en el mismo código fuente) para producir diferentes binarios, entonces no podría sentirse seguro de la calidad de cada binario, si no se probaron en su propio sistema operativo / arquitectura.
El maravilloso proyecto de compilador FreePascal de código abierto tiene un eslogan de "escribir una vez, compilar en cualquier lugar". Si bien esto es cierto, no esperaría ver a ninguna empresa de software de buena reputación que lo hiciera y lanzara las aplicaciones sin realizar pruebas exhaustivas en todas las plataformas.
fuente
Fácil: prueba.
Si el código funciona bien en la máquina virtual, puede estar seguro de que se ejecutará en otro hardware que tenga una VM comprobada.
Si compila para diferentes plataformas, tiene que hacer muchas pruebas, porque cada plataforma tiene sus propias peculiaridades.
fuente
¡La portabilidad es mucho más que solo poder compilar el código!
El comportamiento de un programa en C puede ser diferente en plataformas fidderent:
Funciona sin quejas en un chip IBM Power, pero, a través de una excepción en un chip SPARC.
El SO, la versión del SO, la configuración del registro de variables de entorno del sistema de archivos puede afectar la forma en que se ejecutará un programa compilado.
Una JVM prácticamente garantiza el mismo comportamiento sea cual sea el entorno de hardware y software.
fuente