¿Por qué se requieren máquinas virtuales?

8

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.

Em Ae
fuente
55
respondiste tu propia pregunta en el primer párrafo
Ratchet Freak
66
No era nuevo con Java. UCSD Pascal (para un solo ejemplo) hizo lo mismo.
Jerry Coffin
2
@EmAe Y señalaría la ironía de que haya elegido dos sistemas de compilación de CI que se ejecutan en la JVM.
Andrew T Finnell
1
"en lugar de descargar la VM respectiva, descargue el compilador respectivo y ese compilador se encargará del código de máquina + SO para esa máquina específica" , tenemos eso, se llama C. Es la esencia misma de cómo se distribuye el software en Linux, por ejemplo (antes de los administradores de paquetes). Lo siento, pero tengo que votar para cerrar.
GrandmasterB
Cada compilador tiene solo una secuencia de máquinas virtuales en su interior. Puede detener la compilación en algún momento, serializar esa representación intermedia y luego llamar al resto del compilador un "intérprete de máquina virtual". No cambiará nada.
SK-logic

Respuestas:

16

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?

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.

Robert Harvey
fuente
2
Las máquinas virtuales de hoy tienen un mejor rendimiento del que les das crédito. El uso de una máquina virtual le brinda todos los beneficios administrados, como un sistema de tipos, un marco de biblioteca y recolección de basura de forma gratuita.
Robert Harvey
3
@Robert: Tenga en cuenta que no todo el código nativo es C o C ++. No son lentos para escribir programas porque son nativos y, por lo tanto, "se ocupan de muchas complejidades de bajo nivel"; son lentos para escribir programas porque son lenguajes mal diseñados que no hacen un buen trabajo al tratar con cosas de bajo nivel. Pero puede obtener un rendimiento de nivel C de Delphi con la misma facilidad de desarrollo que se encuentra en los lenguajes administrados.
Mason Wheeler
77
@ Andrew: La gente puede decir que Java es rápido de acuerdo con algún punto de referencia que prueba algún algoritmo en algún caso aislado. Pero luego va y ejecuta un programa Java real, como OpenOffice, y se tarda 30 segundos en abrir el cuadro de diálogo Guardar, en comparación con aproximadamente 1-2 segundos en el código nativo, y llegan a la conclusión de que toda esa velocidad teórica en el los puntos de referencia no significan nada; Java sigue siendo lento.
Mason Wheeler
2
@MasonWheeler A medida que eliminó su comentario y se expandió en uno nuevo. La abundancia de personas que pueden salirse con la "programación" en el lenguaje Java es significativamente mayor porque la barrera de entrada para C y C ++ es prohibitiva, por lo tanto, programas realmente malos. Java no es lento y la evidencia anecdótica de un conjunto de aplicaciones horrible no demuestra lo contrario.
Andrew T Finnell
10
@MasonWheeler: Solo para aclarar un error común: OpenOffice no está escrito en Java, sino principalmente en C ++. Solo necesita Java para algunas funciones especiales. Ver wiki.services.openoffice.org/wiki/Java_and_OpenOffice.org .
sleske
12

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.

Andrew T Finnell
fuente
No entiendes mi pregunta. No tengo dudas sobre el rendimiento del código cuando se ejecuta en la máquina virtual específica. La pregunta es por qué usar VM cuando podemos tener un compilador para compilarlo para una máquina específica.
Em Ae
2
@EmAe La VM lo compila para la máquina específica. Se llama un JIT. Le recomiendo que lea sobre esto.
Andrew T Finnell
1
De acuerdo, estoy confundido aquí. Corrígeme y ayúdame. Entonces, ¿el compilador compila para una máquina específica incluso cuando se elige para ejecutarse en VM? Si ese código compilado (digamos JAVA, por ejemplo) se transfiere a una VM en UNIX / Mac / Ubuntu, ¿es necesario volver a compilarlo? NO. Porque para VM el nativo es BYTE-CODE y ese BYTE-CODE luego se convierte usando JIT. 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
Em Ae
La misma razón por la que no reescribe todo el código una y otra vez en el ensamblaje. Reutilizar.
Andrew T Finnell
2
Además, nadie dice que tiene que tomar esos cuatro pasos, siempre puede usar un compilador AOT. gcc.gnu.org/java
TomJ
7

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.

medkg15
fuente
3

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.

papilla
fuente
2

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.

nikie
fuente
2

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?

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:

  • Solaris x64
  • Solaris x86
  • Solaris SPARC
  • Windows x86
  • Windows x64
  • Linux x86
  • Linux x64
  • Mac OS
  • BSD

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.

Gavin Coates
fuente
1

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.

Crollster
fuente
Todavía cuestionaría la validez de las pruebas en una plataforma y la implementación en otra, incluso cuando se usa una VM. Está implementando en dos implementaciones de VM diferentes, una puede contener un error específico del sistema que no está presente en la otra. Las posibilidades son escasas, pero sigue siendo una buena práctica probar e implementar en sistemas idénticos.
Gavin Coates
TBH, realmente estoy de acuerdo, pero ¿es más fácil probar una VM que un compilador? (es decir, ¿es una máquina virtual más confiable, ya que es más fácil de probar, o es igual de arriesgado?) Al menos con las máquinas virtuales, usted sabe que el código ha sido utilizado por miles (¿millones?) de otros, mientras que solo usted (y un un puñado de otros) han compilado su aplicación.
Crollster
Crollster: Estoy de acuerdo. en el mundo real usarías hardware y software idénticos, pero esto a veces no es posible. El uso de una plataforma diferente puede ser aceptable en la mayoría de los casos, pero vale la pena tener en cuenta que existe un riesgo de error, aunque relativamente pequeño.
Gavin Coates
1

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.

Pieter B
fuente
0

¡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:

int  i;
char c[6];
int * p;

p = &c[2];
p = p + 1;

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.

James Anderson
fuente