Busqué en Google y busqué en el sitio web de Go, pero parece que no puedo encontrar una explicación para los extraordinarios tiempos de construcción de Go. ¿Son productos de las características del lenguaje (o la falta de ellas), un compilador altamente optimizado u otra cosa? No estoy tratando de promocionar Go; Tengo curiosidad.
performance
compiler-construction
build-process
go
Evan Kroske
fuente
fuente
Respuestas:
Análisis de dependencia.
Las preguntas frecuentes de Go solían contener la siguiente oración:
Si bien la frase ya no se encuentra en las preguntas frecuentes, este tema se desarrolla en la charla Go en Google , que compara el enfoque de análisis de dependencia de C / C ++ y Go.
Esa es la razón principal de la compilación rápida. Y esto es por diseño.
fuente
Creo que no es que los compiladores de Go sean rápidos , es que otros compiladores son lentos .
Los compiladores C y C ++ tienen que analizar enormes cantidades de encabezados; por ejemplo, compilar C ++ "hello world" requiere compilar 18 mil líneas de código, ¡lo que equivale a casi medio megabyte de fuentes!
Los compiladores Java y C # se ejecutan en una VM, lo que significa que antes de que puedan compilar algo, el sistema operativo debe cargar toda la VM, luego deben compilarse JIT desde el código de bytes hasta el código nativo, todo lo cual lleva algún tiempo.
La velocidad de compilación depende de varios factores.
Algunos idiomas están diseñados para compilarse rápidamente. Por ejemplo, Pascal fue diseñado para ser compilado usando un compilador de un solo paso.
Los compiladores también se pueden optimizar. Por ejemplo, el compilador Turbo Pascal fue escrito en un ensamblador optimizado a mano que, combinado con el diseño del lenguaje, resultó en un compilador realmente rápido que funciona en hardware de clase 286. Creo que incluso ahora, los compiladores Pascal modernos (por ejemplo, FreePascal) son más rápidos que los compiladores Go.
fuente
Existen múltiples razones por las cuales el compilador Go es mucho más rápido que la mayoría de los compiladores C / C ++:
Razón principal : la mayoría de los compiladores C / C ++ exhiben diseños excepcionalmente malos (desde la perspectiva de la velocidad de compilación). Además, desde la perspectiva de la velocidad de compilación, algunas partes del ecosistema C / C ++ (como los editores en los que los programadores escriben sus códigos) no están diseñadas teniendo en cuenta la velocidad de compilación.
Razón principal : la rápida velocidad de compilación fue una elección consciente en el compilador Go y también en el idioma Go
El compilador Go tiene un optimizador más simple que los compiladores C / C ++
A diferencia de C ++, Go no tiene plantillas ni funciones en línea. Esto significa que Go no necesita realizar ninguna instancia de plantilla o función.
El compilador Go genera un código de ensamblaje de bajo nivel antes y el optimizador funciona en el código de ensamblaje, mientras que en un compilador C / C ++ típico, la optimización pasa a trabajar en una representación interna del código fuente original. La sobrecarga adicional en el compilador C / C ++ proviene del hecho de que la representación interna necesita ser generada.
La vinculación final (5l / 6l / 8l) de un programa Go puede ser más lenta que la vinculación de un programa C / C ++, porque el compilador Go está pasando por todo el código de ensamblaje utilizado y tal vez también está haciendo otras acciones adicionales que C / C ++ los enlazadores no están haciendo
Algunos compiladores C / C ++ (GCC) generan instrucciones en forma de texto (para pasar al ensamblador), mientras que el compilador Go genera instrucciones en forma binaria. Es necesario realizar un trabajo adicional (pero no mucho) para transformar el texto en binario.
El compilador Go apunta solo a un pequeño número de arquitecturas de CPU, mientras que el compilador GCC apunta a un gran número de CPU
Los compiladores que fueron diseñados con el objetivo de una alta velocidad de compilación, como Jikes, son rápidos. En una CPU de 2 GHz, Jikes puede compilar más de 20000 líneas de código Java por segundo (y el modo incremental de compilación es aún más eficiente).
fuente
La eficiencia de la compilación fue un objetivo de diseño importante:
Las preguntas frecuentes sobre el lenguaje son bastante interesantes con respecto a las características específicas del lenguaje relacionadas con el análisis:
fuente
aType
una referencia variable, y más adelante en la fase de análisis semántico cuando descubre que no está imprimiendo un error significativo en ese momento.Si bien la mayor parte de lo anterior es cierto, hay un punto muy importante que no se mencionó realmente: la gestión de dependencias.
Go solo necesita incluir los paquetes que está importando directamente (como los que ya importaron lo que necesitan). Esto está en marcado contraste con C / C ++, donde cada archivo comienza, incluidos los encabezados x, que incluyen los encabezados y, etc.
fuente
Una buena prueba para la eficiencia de traducción de un compilador es la autocompilación: ¿cuánto tiempo tarda un compilador determinado en compilarse? Para C ++ lleva mucho tiempo (¿horas?). En comparación, un compilador Pascal / Modula-2 / Oberon se compilaría en menos de un segundo en una máquina moderna [1].
Go se ha inspirado en estos idiomas, pero algunas de las razones principales de esta eficiencia incluyen:
Una sintaxis claramente definida que es matemáticamente sólida, para un escaneo y análisis eficiente.
Un lenguaje compilado de forma segura y estático que utiliza compilación separada con dependencia y verificación de tipo a través de los límites del módulo, para evitar la relectura innecesaria de los archivos de encabezado y la compilación de otros módulos, en lugar de la compilación independiente como en C / C ++ donde el compilador no realiza tales comprobaciones de módulo cruzado (de ahí la necesidad de volver a leer todos esos archivos de encabezado una y otra vez, incluso para un simple programa "hello world" de una línea).
Una implementación eficiente del compilador (p. Ej., Análisis de arriba a abajo recursivo de un solo paso), que por supuesto es de gran ayuda en los puntos 1 y 2 anteriores.
Estos principios ya se conocían y se implementaron por completo en los años setenta y ochenta en idiomas como Mesa, Ada, Modula-2 / Oberon y varios otros, y solo ahora (en los años 2010) están llegando a idiomas modernos como Go (Google) , Swift (Apple), C # (Microsoft) y varios otros.
Esperemos que esto sea pronto la norma y no la excepción. Para llegar allí, deben suceder dos cosas:
Primero, los proveedores de plataformas de software como Google, Microsoft y Apple deberían comenzar alentando a los desarrolladores de aplicaciones a usar la nueva metodología de compilación, mientras les permiten reutilizar su base de código existente. Esto es lo que Apple ahora está tratando de hacer con el lenguaje de programación Swift, que puede coexistir con Objective-C (ya que usa el mismo entorno de tiempo de ejecución).
En segundo lugar, las plataformas de software subyacentes deberían reescribirse eventualmente con el tiempo utilizando estos principios, mientras se rediseña simultáneamente la jerarquía de módulos en el proceso para hacerlos menos monolíticos. Por supuesto, esta es una tarea gigantesca y bien puede llevar la mayor parte de una década (si son lo suficientemente valientes como para hacerlo realmente, lo cual no estoy del todo seguro en el caso de Google).
En cualquier caso, es la plataforma la que impulsa la adopción del lenguaje, y no al revés.
Referencias
[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf , página 6: "El compilador se compila en unos 3 segundos". Esta cita es para una placa de desarrollo Xilinx Spartan-3 FPGA de bajo costo que funciona a una frecuencia de reloj de 25 MHz y presenta 1 MByte de memoria principal. A partir de este, se puede extrapolar fácilmente a "menos de 1 segundo" para un procesador moderno que funcione a una frecuencia de reloj muy superior a 1 GHz y varios GBytes de memoria principal (es decir, varios órdenes de magnitud más potentes que la placa FPGA Xilinx Spartan-3), incluso cuando se tienen en cuenta las velocidades de E / S. Ya en 1990, cuando Oberon se ejecutaba en un procesador NS32X32 de 25MHz con 2-4 MB de memoria principal, el compilador se compiló en solo unos segundos. La noción de esperar realmentepara el programador terminar un ciclo de compilación era completamente desconocido para los programadores de Oberon incluso en aquel entonces. Para los programas típicos, siempre lleva más tiempo quitar el dedo del botón del mouse que activó el comando de compilación que esperar a que el compilador complete la compilación que acaba de activarse. Fue realmente una gratificación instantánea, con tiempos de espera cercanos a cero. Y la calidad del código producido, aunque no siempre estuvo completamente a la par con los mejores compiladores disponibles en ese momento, fue notablemente buena para la mayoría de las tareas y bastante aceptable en general.
fuente
Go fue diseñado para ser rápido, y se nota.
Tenga en cuenta que GO no es el único idioma con tales características (los módulos son la norma en los idiomas modernos), pero lo hicieron bien.
fuente
Citando el libro " The Go Programming Language " de Alan Donovan y Brian Kernighan:
fuente
La idea básica de compilación es realmente muy simple. Un analizador de descenso recursivo, en principio, puede funcionar a velocidad de E / S. La generación de código es básicamente un proceso muy simple. Una tabla de símbolos y un sistema de tipo básico no es algo que requiera muchos cálculos.
Sin embargo, no es difícil ralentizar un compilador.
Si hay una fase de preprocesador, con directivas de inclusión de múltiples niveles , definiciones de macro y compilación condicional, tan útiles como esas cosas son, no es difícil cargarlo. (Por ejemplo, estoy pensando en los archivos de encabezado de Windows y MFC). Es por eso que los encabezados precompilados son necesarios.
En términos de optimización del código generado, no hay límite para la cantidad de procesamiento que se puede agregar a esa fase.
fuente
Simplemente (en mis propias palabras), porque la sintaxis es muy fácil (analizar y analizar)
Por ejemplo, no significa herencia de tipo, no análisis problemático para averiguar si el nuevo tipo sigue las reglas impuestas por el tipo base.
Por ejemplo, en este ejemplo de código: "interfaces" el compilador no va y verifica si el tipo deseado implementa la interfaz dada mientras analiza ese tipo. Solo hasta que se use (y SI se usa) se realiza la verificación.
Otro ejemplo, el compilador le dice si está declarando una variable y no la está usando (o si se supone que tiene un valor de retorno y no lo está)
Lo siguiente no se compila:
Este tipo de aplicación y principios hacen que el código resultante sea más seguro, y el compilador no tiene que realizar validaciones adicionales que el programador puede hacer.
En general, todos estos detalles hacen que un lenguaje sea más fácil de analizar, lo que resulta en compilaciones rápidas.
De nuevo, en mis propias palabras.
fuente
Creo que Go fue diseñado en paralelo con la creación del compilador, por lo que fueron mejores amigos desde el nacimiento. (OMI)
fuente
¿Qué más?
fuente