Cuando creamos una aplicación, generalmente solo prestamos atención a nuestro código o bibliotecas, y no nos importa cómo se ejecutará, compilará o interpretará ese código.
Ambas opciones tienen sus ventajas y desventajas, principalmente en cuanto a la distribución y rendimiento de nuestro código. Un código compilado está hecho para un solo sistema, por lo que si lo vamos a hacer disponible para más de un sistema, tendremos que mantener un código binario para cada uno. Por otro lado, el código interpretado se puede distribuir fácilmente entre múltiples sistemas, pero no funciona tan bien como el código compilado. Los lenguajes compilados como C, C++, Fortran, Delphi y Pascal generalmente funcionan mejor, mientras que los lenguajes interpretados como C#, JavaScript, Python, PHP y Ruby tienden a ser más fáciles de portar, lo que les permite ejecutarse en múltiples sistemas.
¿Qué es un compilador y cuáles son sus principales tareas?
El compilador es un programa que lee y analiza el código fuente de la aplicación, es decir, el código que escribimos, y genera un código binario que se puede ejecutar desde él. Los compiladores suelen ser programas con lógica compleja, ya que utilizan muchas técnicas para acelerar la aplicación y reducir los requisitos del sistema donde se ejecutará la aplicación. Las principales tareas de los compiladores son:
Preprocesamiento
Este es el primer paso para un compilador, donde se incluye el código fuente, se analizan los errores de sintaxis y se vuelven a emplear y procesar macros o definiciones. Este paso se da mucho en lenguajes como C y C++.
Compilacion
En este paso, el código fuente se convierte en código ensamblador, muy similar al código máquina o código binario. Sin embargo, todavía contiene referencias a archivos externos, por lo que no se puede utilizar.
Assembly Con el código Assembly listo, pasa por un convertidor, llamado ensamblador, para convertirse en un código binario hecho exclusivamente para un solo sistema.
Linker
Esta es la última etapa del compilador, donde las bibliotecas, ya compiladas, se agregan a nuestro código binario, lo que permite la creación de un archivo binario ejecutable.
Algunos compiladores contienen más tareas, como optimización y detección de errores comunes, entre otras opciones, dejando que el creador del compilador las defina todas, así podemos tener varios compiladores para un mismo lenguaje, que pasan por diferentes procesos y terminan hasta generar códigos binarios en distintos lenguajes, como es el caso de los lenguajes C y C++, que cuentan con más de 30 compiladores diferentes.
Código binário
Los procesadores no entienden las palabras, solo contienen circuitos que pueden realizar acciones como matemáticas y leer y escribir en la memoria. Entonces, para usar estas operaciones, debemos elegir los circuitos, también llamados instrucciones, y luego pasarles los parámetros sobre los que queremos actuar.
Podemos pensar en las instrucciones como si fueran funciones predefinidas que aceptan algunos argumentos, y el trabajo del compilador es hacer que todo el código sea ejecutado por estas funciones. Los circuitos se activan, usan y borran durante la ejecución de un programa porque el archivo binario de este programa contiene las operaciones que queremos realizar, siempre usando números binarios, 0 y 1.
Usemos un ejemplo para que sea un poco más fácil visualizar los códigos binarios y su ejecución en los circuitos del procesador. Usando C para escribir el código fuente, tenemos:
int main()
{ // función main con un retorno de tipo numérico
int x; // creación de variables x
x = 3; // establece el valor de x
}
Cuando lo pasemos por un compilador, tendremos un archivo binario que se verá así:
111001011000100101001000010101010000001111111100010001011100011110111000000000000000000000000000000000000000000000000000000000001100001101011101
Este es el contenido de un archivo binario. Para ver o editar este contenido, necesitamos un editor especial, ya que los editores de texto intentan poner este archivo en alguna codificación, como UTF-8 o ASCII, para mostrarnos los caracteres.
Podemos ver lo que significan estos números al traducir el archivo binario a Assembly:
pushq %rbp //inicia el programa con la operación de 64-bit
movq %rsp, %rbp //transfiere el valor presente en rsp al comienzo del programa
movl $3, -4(%rbp) //creación de variables y asignación de valor 3
movl $3, $eax //transfiere el valor 3 a la posición eax
popq %eax //copiar el valor 3 al final del programa
ret // cierra el programa
La primera palabra de cada línea es la instrucción que se debe activar dentro del procesador, una vez que tengamos los parámetros. Como es mucho más laborioso escribir en código ensamblador o binario, generalmente usamos un lenguaje de nivel superior, como C, C + +.
Además, el código binario varía de un sistema a otro, por lo que los códigos binarios creados para Windows no funcionan en Linux, ya que el sistema operativo no los reconoce como ejecutables.
También tenemos diferentes arquitecturas de procesadores, como x86-x64, que se usa en la mayoría de las computadoras, ARM, que se usa en teléfonos móviles y recientemente en algunos servidores, y RISC-V, que está ganando terreno en las aplicaciones de IoT y está tratando de ingresar a otras mercados. Cada una de estas arquitecturas tiene diferentes códigos binarios, ya que contienen diferentes circuitos, la x86-x64 contiene aproximadamente 3700 instrucciones, mientras que el ARM tiene aproximadamente 500 instrucciones y el RISC-V con 47 instrucciones.
Lenguajes interpretados
Si bien la compilación generalmente hace que los programas sean eficientes en términos de consumo de recursos, dificulta la transferencia de código entre múltiples sistemas. Pensando en cómo solucionar esto, se crearon lenguajes que no se compilan durante el desarrollo, sino durante la ejecución, lenguajes interpretados.
Los lenguajes interpretados no necesitan pasar por el proceso de compilación, lo que acelera enormemente la velocidad de desarrollo, ya que los programas grandes y complejos pueden tardar más de 30 minutos en completarse y todo el programa debe recompilarse con cada cambio en el código.
El proceso de interpretación del lenguaje consta de varios pasos y muchos se realizan dentro de máquinas virtuales, como es el caso de python. Sin embargo, todavía es necesario usar solo las instrucciones presentes en la máquina física y, por lo tanto, los intérpretes terminan compilando el código, pero lo hacen durante la ejecución.
Algunas lenguajes interpretadas son PHP, JavaScript y Python, cada una con desarrollo enfocado, pero no exclusivo, a un público objetivo, como JavaScript en el Front-end con navegadores y en el Back-end con NodeJS o Deno.
Los lenguajes interpretados son más fáciles de trasladar de un sistema a otro, pero necesitan una capa intermedia para traducir los comandos del programa a código binario, como el intérprete de Python a Python y el navegador a JavaScript. Este proceso crea una barrera con respecto al desempeño de estos lenguajes.
En algunos casos, algunas bibliotecas funcionan mejor que el idioma nativo porque están escritas en un lenguaje compilado, como es el caso de las bibliotecas numPy y sciPy para Python.
En general, elegir un lenguaje interpretado permite que tu código se ejecute en varias computadoras, con diferentes sistemas o configuraciones, y también acelera mucho el período de desarrollo, pero tiende a ser más lento y consumen más de la máquina en la que se ejecutan.
Entonces, para algunas aplicaciones se prefieren lenguajes interpretados, como es el caso de JavaScript para ejecutar código en el navegador, ya que un lenguaje compilado necesitaría tener varias versiones, dependiendo del sistema cliente. Por tanto, podrían existir incompatibilidades si no se transfiere la configuración del cliente, por motivos de privacidad o seguridad.
También se recomienda comenzar a estudiar programación utilizando lenguajes interpretados, ya que no se dedica mucho tiempo a compilar cada cambio de código, lo que hace que la experiencia sea más rápida y fluida.
Java
Java es un lenguaje diferente a la mayoría en cuanto a ser compilado o interpretado, es interpretado y compilado al mismo tiempo. Esto permite que Java se ejecute en muchos dispositivos diferentes con un solo ejecutable y una sola compilación y, además, no tiene una gran penalización de tiempo de ejecución.
Como Java se ejecuta dentro de la Máquina Virtual de Java (JVM), o la Máquina Virtual de Java, puede considerarse un lenguaje interpretado. Al mismo tiempo, la JVM no utiliza código fuente, sino una versión compilada para él.
Con esta técnica es posible ejecutar el código en cualquier sistema que soporte la JVM y, al mismo tiempo, no tiene una penalización de rendimiento muy alta a la hora de la ejecución, como ocurre con otros lenguajes interpretados, ya que el trabajo de optimización del código fuente ya se ha creado y el archivo generado está más cerca del lenguaje de máquina.
Al mismo tiempo, necesitamos el intérprete que los lenguajes compilados no necesitan, y también dedicamos tiempo a cada compilación, lo que ralentiza el desarrollo.
Conclusión
El compilador tiene un trabajo muy importante tanto en los lenguajes que se compilan como en los que se interpretan, ya que todos los comandos deben ser transformados a código binario para poder ser procesados. Los primeros compiladores, que estaban escritos en lenguaje ensamblador, permitieron crear nuevos programas y compiladores mejores y más complejos, acelerando el desarrollo y automatizando la tarea de analizar y traducir la lógica del programa en instrucciones de máquina.
Leonardo Sartorello.
Leonardo es desarrollador y capacitador en Alura con un enfoque principal en DevOps y Cloud, con experiencia en virtualización, contenedorización, infraestructura como código e IoT.
Traducido para Alura Latam por Rafaela Rocha.
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Puedes realizar el pago de tus planes en moneda local en los siguientes países:
País | |||||||
---|---|---|---|---|---|---|---|
Plan Semestral |
487.37
BOB |
69289.36
CLP |
307472.10
COP |
65.90
USD |
264.35
PEN |
1435.53
MXN |
2978.57
UYU |
Plan Anual |
738.82
BOB |
105038.04
CLP |
466107.17
COP |
99.90
USD |
400.74
PEN |
2176.17
MXN |
4515.32
UYU |
Acceso a todos
los cursos
Estudia las 24 horas,
dónde y cuándo quieras
Nuevos cursos
cada semana