Solución:
En esta publicación, “máquina virtual” se refiere a procesar máquinas virtuales, no a máquinas virtuales del sistema como Qemu o Virtualbox. Una máquina virtual de proceso es simplemente un programa que proporciona un entorno de programación general, un programa que se puede programar.
Java tiene un intérprete y una máquina virtual, y Python tiene una máquina virtual y un intérprete. La razón por la que “máquina virtual” es un término más común en Java y “intérprete” es un término más común en Python tiene mucho que ver con la principal diferencia entre los dos lenguajes: escritura estática (Java) vs escritura dinámica (Python). En este contexto, “tipo” se refiere a tipos de datos primitivos, tipos que sugieren el tamaño de almacenamiento en memoria de los datos. La máquina virtual Java lo tiene fácil. Requiere que el programador especifique el tipo de datos primitivo de cada variable. Esto proporciona información suficiente para que el código de bytes de Java no solo sea interpretado y ejecutado por la máquina virtual de Java, sino incluso para que se compile en las instrucciones de la máquina. La máquina virtual Python es más compleja en el sentido de que asume la tarea adicional de pausar antes de la ejecución de cada operación para determinar los tipos de datos primitivos para cada variable o estructura de datos involucrada en la operación. Python libera al programador de pensar en términos de tipos de datos primitivos y permite que las operaciones se expresen en un nivel superior. El precio de esta libertad es el rendimiento. “Intérprete” es el término preferido para Python porque tiene que hacer una pausa para inspeccionar los tipos de datos, y también porque la sintaxis comparativamente concisa de los lenguajes de tipado dinámico es una buena opción para las interfaces interactivas. No existe una barrera técnica para construir una interfaz Java interactiva, pero intentar escribir cualquier código escrito de forma estática de manera interactiva sería tedioso, por lo que simplemente no se hace de esa manera.
En el mundo de Java, la máquina virtual se roba el show porque ejecuta programas escritos en un lenguaje que en realidad se puede compilar en instrucciones de la máquina, y el resultado es velocidad y eficiencia de recursos. El código de bytes de Java puede ser ejecutado por la máquina virtual de Java con un rendimiento que se acerca al de los programas compilados, en términos relativos. Esto se debe a la presencia de información de tipo de datos primitivos en el código de bytes. La máquina virtual Java coloca a Java en una categoría propia:
lenguaje portátil interpretado de tipado estático
Lo siguiente más cercano es LLVM, pero LLVM opera a un nivel diferente:
lenguaje ensamblador interpretado portátil
El término “código de bytes” se utiliza tanto en Java como en Python, pero no todos los códigos de bytes son iguales. bytecode es solo el término genérico para lenguajes intermedios utilizados por compiladores / intérpretes. Incluso los compiladores de C como gcc usan un lenguaje intermedio (o varios) para hacer el trabajo. El código de bytes de Java contiene información sobre tipos de datos primitivos, mientras que el código de bytes de Python no. En este sentido, la máquina virtual Python (y Bash, Perl, Ruby, etc.) es realmente fundamentalmente más lenta que la máquina virtual Java, o más bien, simplemente tiene más trabajo por hacer. Es útil considerar qué información está contenida en diferentes formatos de código de bytes:
- llvm: registros de cpu
- Java: tipos de datos primitivos
- Pitón: tipos definidos por el usuario
Para dibujar una analogía del mundo real: LLVM trabaja con átomos, la máquina virtual Java trabaja con moléculas y la máquina virtual Python trabaja con materiales. Dado que todo debe eventualmente descomponerse en partículas subatómicas (operaciones de la máquina real), la máquina virtual Python tiene la tarea más compleja.
Los intérpretes / compiladores de lenguajes de tipado estático simplemente no tienen el mismo equipaje que los intérpretes / compiladores de lenguajes de tipado dinámico. Los programadores de lenguajes de tipado estático tienen que tomar el relevo, para lo cual la recompensa es el rendimiento. Sin embargo, así como todas las funciones no deterministas son secretamente deterministas, también todos los lenguajes escritos dinámicamente se escriben estáticamente en secreto. Por lo tanto, las diferencias de rendimiento entre las dos familias de idiomas deberían estabilizarse en el momento en que Python cambia su nombre a HAL 9000.
Las máquinas virtuales de lenguajes dinámicos como Python implementan alguna máquina lógica idealizada y no necesariamente se corresponden muy de cerca con ningún hardware físico real. La máquina virtual Java, por el contrario, es más similar en funcionalidad a un compilador de C clásico, excepto que en lugar de emitir instrucciones de máquina, ejecuta rutinas integradas. En Python, un entero es un objeto de Python con un montón de atributos y métodos adjuntos. En Java, un int es un número designado de bits, generalmente 32. No es realmente una comparación justa. Los enteros de Python realmente deberían compararse con la clase Java Integer. El tipo de datos primitivo “int” de Java no se puede comparar con nada en el lenguaje Python, porque el lenguaje Python simplemente carece de esta capa de primitivos, al igual que el código de bytes de Python.
Debido a que las variables de Java se escriben explícitamente, uno puede esperar razonablemente que algo como el rendimiento de Jython esté en el mismo estadio que cPython. Por otro lado, es casi seguro que una máquina virtual Java implementada en Python sea más lenta que el barro. Y no espere que a Ruby, Perl, etc., les vaya mejor. No fueron diseñados para hacer eso. Fueron diseñados para “scripting”, que es como se llama la programación en un lenguaje dinámico.
Cada operación que tiene lugar en una máquina virtual eventualmente tiene que llegar al hardware real. Las máquinas virtuales contienen rutinas precompiladas que son lo suficientemente generales como para ejecutar cualquier combinación de operaciones lógicas. Es posible que una máquina virtual no esté emitiendo nuevas instrucciones de máquina, pero ciertamente está ejecutando sus propias rutinas una y otra vez en secuencias arbitrariamente complejas. La máquina virtual Java, la máquina virtual Python y todas las demás máquinas virtuales de uso general son iguales en el sentido de que pueden ser persuadidas para que realicen cualquier lógica que pueda imaginar, pero son diferentes en términos de las tareas que realizan. que asumen y las tareas que dejan al programador.
Psyco para Python no es una máquina virtual Python completa, sino un compilador justo a tiempo que secuestra la máquina virtual Python normal en puntos en los que cree que puede compilar algunas líneas de código, principalmente bucles donde cree que el tipo primitivo de algunas La variable permanecerá constante incluso si el valor cambia con cada iteración. En ese caso, puede prescindir de algunas determinaciones de tipo incesante de la máquina virtual normal. Sin embargo, debe tener un poco de cuidado, no sea que saque el tipo de debajo de los pies de Psyco. Sin embargo, Pysco generalmente sabe que debe recurrir a la máquina virtual normal si no está completamente seguro de que el tipo no cambiará.
La moraleja de la historia es que la información de tipos de datos primitivos es realmente útil para un compilador / máquina virtual.
Finalmente, para ponerlo todo en perspectiva, considere esto: un programa Python ejecutado por un intérprete / máquina virtual Python implementado en Java que se ejecuta en un intérprete / máquina virtual Java implementado en LLVM que se ejecuta en una máquina virtual qemu que se ejecuta en un iPhone.
enlace permanente
Una máquina virtual es un entorno informático virtual con un conjunto específico de instrucciones atómicas bien definidas que se admiten independientemente de cualquier lenguaje específico y generalmente se considera como un espacio aislado en sí mismo. La VM es análoga a un conjunto de instrucciones de una CPU específica y tiende a funcionar a un nivel más fundamental con bloques de construcción muy básicos de tales instrucciones (o códigos de bytes) que son independientes de la siguiente. Una instrucción se ejecuta de forma determinista basándose únicamente en el estado actual de la máquina virtual y no depende de información en otra parte del flujo de instrucciones en ese momento.
Por otro lado, un intérprete es más sofisticado porque está diseñado para analizar un flujo de alguna sintaxis que es de un idioma específico y de una gramática específica que debe decodificarse en el contexto de los tokens circundantes. No puede mirar cada byte o incluso cada línea de forma aislada y saber exactamente qué hacer a continuación. Los tokens en el idioma no se pueden tomar de forma aislada como en relación con las instrucciones (códigos de bytes) de una máquina virtual.
Un compilador de Java convierte el lenguaje Java en una secuencia de código de bytes no diferente a un compilador de C que convierte programas de lenguaje C en código ensamblador. Un intérprete, por otro lado, no convierte realmente el programa en una forma intermedia bien definida, simplemente toma las acciones del programa como una cuestión del proceso de interpretación de la fuente.
Otra prueba de la diferencia entre una máquina virtual y un intérprete es si piensa que es independiente del idioma. Lo que conocemos como Java VM no es realmente específico de Java. Puede crear un compilador a partir de otros lenguajes que den como resultado códigos de bytes que se pueden ejecutar en la JVM. Por otro lado, no creo que realmente se nos ocurra “compilar” algún otro lenguaje que no sea Python en Python para que el intérprete de Python lo interprete.
Debido a la sofisticación del proceso de interpretación, este puede ser un proceso relativamente lento … específicamente analizar e identificar los tokens de idioma, etc. y comprender el contexto de la fuente para poder llevar a cabo el proceso de ejecución dentro del intérprete. Para ayudar a acelerar dichos lenguajes interpretados, aquí es donde podemos definir formas intermedias de código fuente pre-analizado y pre-tokenizado que se interpreta más fácilmente directamente. Este tipo de forma binaria todavía se interpreta en el momento de la ejecución, está comenzando desde una forma mucho menos legible por humanos para mejorar el rendimiento. Sin embargo, la lógica que ejecuta ese formulario no es una máquina virtual, porque esos códigos aún no se pueden tomar de forma aislada: el contexto de los tokens circundantes aún importa, ahora están en una forma diferente más eficiente en la computadora.
Probablemente una de las razones de la terminología diferente es que normalmente se piensa en alimentar al intérprete de Python con código fuente legible por humanos en bruto y no preocuparse por el código de bytes y todo eso.
En Java, debe compilar explícitamente el código de bytes y luego ejecutar solo el código de bytes, no el código fuente en la máquina virtual.
Aunque Python usa una máquina virtual bajo las sábanas, desde la perspectiva del usuario, uno puede ignorar este detalle la mayor parte del tiempo.