Interpretación Versus Compilación

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Prefacio

Durante mucho tiempo, incluso hoy en día, voy a contar mi lenguaje de programación principal como PHP (aunque eso está cambiando a medida que pasa el tiempo, tal vez sea Python, con JavaScript un segundo distante.) Desde que aprendí los huesos de PHP en un Intento de ponerse al corriente de mi sitio Clasheerian Order que heredé de algunas personas bastante infelices (por desgracia les hice infeliz) que estaba ejecutando PHPNuke, siempre he sido un fan de PHP. No estoy seguro exactamente de lo que era, pero siempre me pareció muy claro. Todos los elementos siempre fueron muy claros, ya que aprendí a usar espacios de nombres, interfaces, rasgos y todas las otras cosas que PHP añadió a lo largo de los años. De hecho, soy una parte de un sitio, o al menos solía ser un colaborador de un sitio, llamado phpclasses.org. He ganado varios “Premios de la Innovación” en su sitio que incluso me consiguió mi copia de trabajo actual de mi Komodo IDE.

Pero me separo de la puerta. Una de las características clave de PHP, tal como está actualmente, es que es un lenguaje interpretado. Por otro lado, algo como C (que en el momento de escribir este documento actualmente estoy cubriendo en otro lugar) fue diseñado para ser un lenguaje compilado. Qué significa eso?

Encontrará algunas personas que consideran si un idioma es usualmente interpretado o compilado dividir idiomas en estas categorías. Sin embargo, la verdad del asunto es todo lo contrario. Si una lengua es compilada o interpretada es realmente una opción independiente de la naturaleza del lenguaje sí mismo. Cualquier lenguaje puede ser interpretado por lo que se conoce como un intérprete, o compilado por lo que se conoce como un compilador. Antes de seguir adelante, debo explicar estos términos, para que no empiece a tener sentido.

El ciclo de vida de un programa

En el uso de cualquier lenguaje de programación hay un cierto ciclo de vida. Usted escribe el programa, entonces ejecuta el programa, encuentra errores, y depura el programa; Por lo tanto, escribir más del programa, fregar, enjuagar, repetir. Lo que nos preocupa hoy es la parte de “ejecutar” del programa.

Cuando se escribe un programa de su intención final es tener las instrucciones, las limitaciones, o lo que tiene que el bosquejo y el detalle en ese programa ser procesado por el ordenador. Como he cubierto en tutoriales anteriores, una computadora se reduce al procesador que va paso a paso la ejecución de instrucciones codificadas en binario que se almacenan en la memoria. ¿Cómo podemos obtener de usted escribiendo “a = 3;” A estas instrucciones codificadas específicas que el procesador puede entender?

Lo hacemos a través de lo que se conoce como el proceso de compilación. Hay una pieza especial de software conocido como un compilador que toma como entrada el programa que ha escrito. A continuación, analiza y pasa a través de cada parte del programa y construye el ensamblaje y el código de máquina específicos para el procesador previsto de esos pasos. A menudo se denomina código objeto. Entretanto, en este espacio hay el enlazador que toma varias partes del programa que se han convertido en código de objeto por separado y los une a todos en un ejecutable, que es un archivo o aplicación en el equipo de destino que se puede ejecutar y ejecutar Para el resultado deseado. Aquí está un diagrama del proceso:

La pieza terminada de este proceso es el ejecutable. Es básicamente una cadena de código de máquina, bits específicos, 1s y 0s, que tienen sentido para el procesador de la computadora específica. Cuando se ejecuta, o decir a la computadora para ejecutar, este ejecutable toma la primera instrucción directamente de ella, sin filtrar o traducir, y comienza el programa y sigue las instrucciones a la carta sin ninguna traducción adicional. Esa es la característica clave del proceso de compilación, su resultado, al ser el ejecutable, no tiene que ser traducido o filtrado para que el procesador tome la primera instrucción y vaya a toda velocidad con ella.

Un lector astuto puede preguntar, ¿de dónde proviene el compilador? Bueno, alguien lo programó. Los compiladores de programación son muy técnicos y pueden ser muy complejos, y de hecho los primeros compiladores se escribieron directamente en código de máquina o mediante ensamblaje. Pero el objetivo del compilador es claro: traducir un programa de entrada en un código de máquina ejecutable para un procesador en particular.

Algunos lenguajes de programación fueron diseñados con este proceso, el de compilación, en mente. C, por ejemplo, fue diseñado para dar poder a los programadores con su facilidad y expresividad, pero finalmente fue diseñado de tal manera que se tradujera fácilmente en código de ensamblaje o máquina. No todos los idiomas tenían esto en mente en su concepción. Por ejemplo, Java estaba destinado a ejecutarse en una especie de entorno “interpretativo”, y Python como otro ejemplo siempre estaba destinado a ser interpretado.

Interpretación del programa

Hay otro método para obtener un programa para ejecutar en un equipo determinado. La alternativa a la compilación es la interpretación. La mayor diferencia entre un compilador y un intérprete está en cómo se relaciona con el programa de entrada. Un compilador, como hemos visto anteriormente, toma todo el programa y lo convierte en código de objeto, básicamente código de máquina recta que el procesador entiende. Un intérprete por otro lado es un ejecutable que lee en su programa paso a paso (o por mucho que necesite leer) y luego procesa, ejecutando inmediatamente, esas instrucciones particulares. En otras palabras, toma su programa paso a paso e inmediatamente realiza esos pasos como parte de su propio ejecutable. Esto significa que no hay ningún código objeto a la mano para el procesador, en una manera de hablar, el intérprete es el código objeto, construido de tal manera que para ser llamado cuando llegue el momento.

Esto rompe la parte de “ejecución” del ciclo de vida que se diagramaba arriba. De hecho, tenemos un nuevo diagrama:

Aquí podemos ver que a diferencia del compilador, que podríamos ignorar una vez que nuestro programa se convierta en un código de máquina ejecutable, debemos tener el programa de intérprete a mano para invocar para ejecutar nuestro programa. De alguna manera, el intérprete se convierte en el procesador. Los programas que se escriben para ser interpretados se llaman comúnmente “escrituras” porque de una manera son las escrituras que otro programa está siguiendo, en comparación con el código directo de la máquina.

Por ejemplo, así es como los idiomas como Python funcionan tradicionalmente. Escribes un programa utilizando las reglas y el lenguaje de programación de Python, y cuando estés listo para ejecutarlo, lo ingresas en el intérprete de Python que posteriormente realiza todos los pasos que has descrito. En la línea de comandos puede escribir algo como esto:

Aquí, Python es el ejecutable que está ejecutando, que a su vez está entrando a su vez lo que está en el archivo myprogram.py  y realizando sus instrucciones. No se puede esperar que el equipo ejecute myprogram.py  sin ejecutar python . Esto es porque myprogram.py  no es código de objeto, no es un código de máquina que el procesador entiende. Como se mencionó anteriormente, es posible compilar programas de Python a objeto o código de máquina y ejecutarlos directamente en el procesador, pero que por lo general implica Compilar e incluir todo el intérprete de Python con él.

Puede parecer tonto a primera vista hacer este paso extra o requerir este programa extra, pero hay beneficios al hacerlo de esta manera. Voy a explorar algunos de ellos en las siguientes secciones.

La naturaleza de un intérprete

Los intérpretes se pueden construir de muchas maneras diferentes. Hay intérpretes que leen en el programa fuente (el que ha escrito) y no hacen ningún procesamiento adicional, simplemente encuentran cadenas de caracteres a la vez y realizan la acción dada. Sin embargo, algunos intérpretes hacen un cierto recopilación de sus propios, pero generalmente en lo que tradicionalmente se llama un código de byte que sólo tiene sentido para el intérprete. Es una especie de lenguaje pseudo-máquina que sólo el intérprete podría entender. Los intérpretes lo hacen por varias razones: es más rápido procesar y es más fácil escribir un ejecutor (la parte del intérprete que realiza las acciones) que lee un código de byte en contraposición a la entrada de origen.

Sin embargo, hay intrepreters que hacen este tipo de código de byte mucho más grande que simplemente un medio de realizar acciones rápidamente. Por ejemplo, el lenguaje de programación Java tradicionalmente se “ejecuta” en lo que se conoce como una máquina virtual. Una máquina virtual es en esencia un ejecutable o parte de un programa que lee un código de byte específico y emula el funcionamiento de un procesador, procesando ese código de bytes como si el procesador de la computadora fuera el procesador virtual. Si alguna vez has utilizado un emulador, estás realmente familiarizado con esta perspectiva. Digamos que tengo un emulador para un sistema de entretenimiento de NIntendo. Cuando carga en un archivo ROM, como por ejemplo Dragon Warrior, se formatea en un código de máquina que sólo el procesador NES entiende. Pero si construyo un procesador falso, que es un procesador en términos abstractos, en la programación, que interpreta ese código de bytes mientras se ejecuta en otro procesador, puedo ejecutar Dragon Warrior en cualquier máquina para la que pueda compilar el emulador.

Eso es poderoso. Y eso es exactamente el concepto que Java aprovecha, y que todos los intérpretes se aprovechan de: si puedo compilar el intérprete, o la máquina virtual ejecutable en una máquina / procesador dado, que cualquier código escrito para ese intérprete o máquina virtual se ejecutará en Esa máquina Así, el infame, “escribir una vez, correr en cualquier lugar.” Cualquier procesador para el que construyo con éxito un intérprete / emulador es un candidato para ejecutar mis programas interpretados / código de byte. Ese es el mayor beneficio de un intérprete sobre un compilador.

Pros y contras

El mayor pro para el proceso de compilación es la velocidad. Ser capaz de compilar cualquier idioma de destino a un código de máquina que el procesador real de la máquina pueda entender elimina cualquier código intermediario, traducción o filtro. Eliminando todo eso (básicamente todo lo que hace un intérprete) ganamos velocidad computacional. Podemos hacer deliberadamente cálculos útiles sin ningún paso adicional, aumentando así el número de ellos posibles en un intervalo de tiempo determinado.

Sin embargo, la con más grande para el proceso de compilación es la especificidad. Cuando compila un programa para ejecutar en un procesador en particular, está creando código de objeto que sólo se ejecutará en ese procesador. Si desea que un programa se ejecute en otra máquina, digamos un procesador ARM en lugar de un procesador basado en Intel 8086, tendrá que re-compilar para ese procesador y, desafortunadamente, a veces la recompilación puede ser un proceso arduo si su nuevo procesador tiene Limitaciones o idiosincrasias no presentes en la primera.

El mayor pro para el negocio de la interpretación es la flexibilidad. No sólo puede ejecutar un programa interpretado en cualquier procesador o plataforma para la que se ha compilado ese intérprete, pero la misma manera en que se escribe un intérprete puede ofrecer flexibilidad adicional. Debido a que en algunos casos, en mi opinión, los intérpretes son más fáciles de entender y codificar que los compiladores (creo que esto se debe al hecho de que la acción a realizar es tan cercana al programa de entrada) a menudo es más fácil re- Implementar intérpretes, agregar funciones adicionales, jugar con implementaciones de cosas como recolectores de basura, y de otra manera extender el lenguaje. Este es un beneficio secundario de los intérpretes junto con el mayor beneficio de “escribir una vez, correr en cualquier lugar”.

Otro beneficio de los intérpretes es que pueden ser más fácilmente reescritos o re-compilados a las plataformas futuras. Escribir un compilador para un procesador que ha añadido un montón de características, o es totalmente diferente de lo que viene antes de que es muy difícil, pero una vez que el compilador está escrito puede compilar un montón de intérpretes y voila, un lenguaje con visión de futuro. No tiene que volver a implementar un intérprete a nivel de base porque un procesador ha cambiado.

Sin embargo, el mayor inconveniente para los intérpretes es la velocidad. Con cada programa hay tanta traducción, filtrado y compilación de código de bytes en curso que se ralentiza y se interpone en el camino de los cálculos deliberados reales. Esta es una gran preocupación para aplicaciones específicas en tiempo real, tales como juegos de alta gama, simulaciones o robótica sensorial. Algunos intérpretes tienen cosas llamadas just-in-time compiladores (JIT) que compilar el programa justo antes de que se ejecuta, pero estos son especiales y por encima y más allá de la construcción de intérpretes. Sin embargo, a medida que los procesadores se vuelven más y más poderosos, esto se convierte en menos preocupante.

Conclusión

Recuerda lo que dije en el prefacio, un lenguaje es independiente de si es interpretado o compilado. Sin embargo, con esto en mente, algunos lenguajes están específicamente diseñados para ser compilados como C, mientras que otros idiomas siempre se suponen interpretados como Java.

Para mí, realmente no importa si algo se compila o interpreta, siempre y cuando se puede hacer el trabajo con la menor cantidad de estrés. Algunos sistemas realmente no ofrecen los requisitos técnicos para interpretar eficazmente (no teóricamente) intérpretes de ejecución tales como algunos microcontroladores más pequeños o procesadores incrustables, y por lo tanto usted debe programarlos con algo directamente compilable capaz como C (algo que estoy haciendo en un Proyecto actual mientras escribo esto). A veces usted quiere hacer algo tan computacional intensivo tan rápido como sea posible, como el reconocimiento de voz exacto de un robot y que el requisito de velocidad puede convertirse en un problema. En otros casos, la velocidad o la potencia computacional pueden no ser tan grandes, y escribir algo como un procesador de lenguaje natural podría ser más fácil en algo como Python o R, que definitivamente se interpretan. O puede que se encuentre en un ecosistema donde las herramientas actuales son tan potentes, como los sistemas back-end de PHP o Node.js, que prácticamente se requiere para usarlos independientemente de si se interpretan o no.

Usted me dice, sin embargo, que prefiere, interpretado o compilado? ¿La idea de tener cálculos adicionales colgando alrededor de lo que usted está intentando hacer le hace loco? ¿O es que tener que preocuparse por los detalles del procesador de bajo nivel lo convierte en tedio que preferiría que la computadora manejara todo eso? ¡Gracias por leer!

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Si usted aprecia este artículo usted puede ser que considere el apoyar de mi Patreon.

Pero si un compromiso mensual es un poco más, lo entiendo, podría considerar comprarme un café.

photo credit: holgalicious Festive Buffet Cookery via photopin (license)

También te podría gustar...

Deja un comentario

A %d blogueros les gusta esto: