performance rich Como o Go compila tão rapidamente?




how do you make a programming language (9)

Eu acho que não é que os compiladores Go sejam rápidos , é que outros compiladores são lentos .

Os compiladores C e C ++ precisam analisar enormes quantidades de cabeçalhos - por exemplo, compilar o C ++ "hello world" requer a compilação de 18k linhas de código, que é quase meio megabyte de fontes!

$ cpp hello.cpp | wc
  18364   40513  433334

Os compiladores Java e C # são executados em uma VM, o que significa que, antes de compilarem qualquer coisa, o sistema operacional precisa carregar a VM inteira, então eles precisam ser compilados em JIT de bytecode para código nativo, o que leva algum tempo.

A velocidade de compilação depende de vários fatores.

Algumas linguagens são projetadas para serem compiladas rapidamente. Por exemplo, o Pascal foi projetado para ser compilado usando um compilador de passagem única.

Compiladores em si podem ser otimizados também. Por exemplo, o compilador Turbo Pascal foi escrito em assembler otimizado a mão, que, combinado com o design da linguagem, resultou em um compilador muito rápido trabalhando em hardware de classe 286. Eu acho que mesmo agora, os compiladores Pascal modernos (por exemplo, FreePascal) são mais rápidos que os compiladores Go.

Eu pesquisei e vasculhei o site Go, mas não consigo encontrar uma explicação para os extraordinários tempos de criação do Go. Eles são produtos dos recursos de linguagem (ou falta deles), um compilador altamente otimizado ou algo mais? Não estou tentando promover o Go; Eu só estou curioso.


Answer #1

Go foi projetado para ser rápido e mostra.

  1. Gerenciamento de dependência: nenhum arquivo de cabeçalho, você só precisa olhar para os pacotes que são importados diretamente (não precisa se preocupar com o que eles importam), assim você tem dependências lineares.
  2. Gramática: a gramática da linguagem é simples, portanto, facilmente analisada. Embora o número de recursos seja reduzido, o próprio código do compilador é pequeno (poucos caminhos).
  3. Nenhuma sobrecarga permitida: você vê um símbolo, sabe a que método se refere.
  4. É trivialmente possível compilar o Go em paralelo, porque cada pacote pode ser compilado independentemente.

Observe que o GO não é a única linguagem com esses recursos (os módulos são a norma nas linguagens modernas), mas eles o fizeram bem.


Answer #2

A idéia básica de compilação é realmente muito simples. Um analisador de descendência recursiva, em princípio, pode ser executado na velocidade limitada de E / S. A geração de código é basicamente um processo muito simples. Uma tabela de símbolos e um sistema de tipo básico não é algo que requer muita computação.

No entanto, não é difícil desacelerar um compilador.

Se houver uma fase de pré-processador, com diretivas de inclusão de vários níveis, definições de macro e compilação condicional, por mais úteis que sejam, não é difícil carregá-las. (Por exemplo, estou pensando nos arquivos de cabeçalho do Windows e do MFC.) É por isso que os cabeçalhos pré-compilados são necessários.

Em termos de otimização do código gerado, não há limite quanto ao processamento pode ser adicionado a essa fase.


Answer #3

Eu acho que o Go foi criado em paralelo com a criação de compiladores, então eles eram melhores amigos desde o nascimento. (IMO)


Answer #4

Existem vários motivos pelos quais o compilador Go é muito mais rápido que a maioria dos compiladores C / C ++:

  • Razão principal : A maioria dos compiladores C / C ++ exibe designs excepcionalmente ruins (da perspectiva da velocidade de compilação). Além disso, do ponto de vista da velocidade de compilação, algumas partes do ecossistema C / C ++ (como editores nos quais os programadores estão escrevendo seus códigos) não são projetadas tendo em mente a velocidade de compilação.

  • Razão principal : a velocidade de compilação rápida foi uma escolha consciente no compilador Go e também na linguagem Go

  • O compilador Go tem um otimizador mais simples que os compiladores C / C ++

  • Ao contrário do C ++, o Go não possui modelos nem funções inline. Isso significa que o Go não precisa executar nenhum modelo ou instanciação de função.

  • O compilador Go gera código de assembly de baixo nível mais cedo e o otimizador trabalha no código assembly, enquanto em um compilador C / C ++ típico a otimização passa o trabalho em uma representação interna do código-fonte original. A sobrecarga extra no compilador C / C ++ vem do fato de que a representação interna precisa ser gerada.

  • Vinculação final (5l / 6l / 8l) de um programa Go pode ser mais lenta do que vincular um programa C / C ++, porque o compilador Go está passando por todo o código de assembly usado e talvez também está fazendo outras ações extras que C / C ++ linkers não estão fazendo

  • Alguns compiladores de C / C ++ (GCC) geram instruções em forma de texto (a serem passadas para o montador), enquanto o compilador Go gera instruções em formato binário. Trabalho extra (mas não muito) precisa ser feito para transformar o texto em binário.

  • O compilador Go tem como alvo apenas um pequeno número de arquiteturas de CPU, enquanto o compilador GCC tem como alvo um grande número de CPUs

  • Compiladores que foram projetados com o objetivo de alta velocidade de compilação, como o Jikes, são rápidos. Em uma CPU de 2 GHz, a Jikes pode compilar mais de 20000 linhas de código Java por segundo (e o modo incremental de compilação é ainda mais eficiente).


Answer #5

Simplesmente (em minhas próprias palavras), porque a sintaxe é muito fácil (analisar e analisar)

Por exemplo, nenhum tipo significa herança, não uma análise problemática para descobrir se o novo tipo segue as regras impostas pelo tipo base.

Por exemplo, neste exemplo de código: "interfaces" o compilador não verifica se o tipo pretendido implementa a interface dada ao analisar esse tipo. Somente até ser usado (e se for usado) a verificação é executada.

Outro exemplo, o compilador informa se você está declarando uma variável e não a usando (ou se você deve manter um valor de retorno e você não está)

O seguinte não compila:

package main
func main() {
    var a int 
    a = 0
}
notused.go:3: a declared and not used

Esse tipo de imposição e principles torna o código resultante mais seguro, e o compilador não precisa executar validações extras que o programador possa fazer.

Em geral, todos esses detalhes tornam a linguagem mais fácil de analisar, o que resulta em compilações rápidas.

Mais uma vez, nas minhas próprias palavras.


Answer #6

Um bom teste para a eficiência da tradução de um compilador é a auto-compilação: quanto tempo leva um determinado compilador para compilar a si mesmo? Para C ++, leva muito tempo (horas?). Em comparação, um compilador Pascal / Modula-2 / Oberon se compilaria em menos de um segundo em uma máquina moderna [1].

O Go foi inspirado por esses idiomas, mas algumas das principais razões para essa eficiência incluem:

  1. Uma sintaxe claramente definida que é matematicamente sólida, para varredura e análise eficientes.

  2. Uma linguagem protegida por tipos e estaticamente compilada que usa compilação separada com dependência e verificação de tipos nos limites do módulo, para evitar a desnecessária releitura de arquivos de cabeçalho e recompilação de outros módulos - ao contrário da compilação independente como em C / C ++ tais verificações de cross-modules não são executadas pelo compilador (daí a necessidade de reler todos os arquivos de cabeçalho repetidas vezes, mesmo para um simples programa one-line "hello world").

  3. Uma implementação eficiente do compilador (por exemplo, análise de passagem única, recursiva-descendente de cima para baixo) - o que, obviamente, é bastante ajudado pelos pontos 1 e 2 acima.

Esses princípios já foram conhecidos e totalmente implementados nas décadas de 1970 e 1980 em idiomas como Mesa, Ada, Modula-2 / Oberon e vários outros, e só agora (na década de 2010) estão entrando em linguagens modernas como o Go (Google). , Swift (Apple), C # (Microsoft) e vários outros.

Vamos esperar que isso seja em breve a norma e não a exceção. Para chegar lá, duas coisas precisam acontecer:

  1. Primeiro, os provedores de plataforma de software, como Google, Microsoft e Apple, devem começar incentivando os desenvolvedores de aplicativos a usar a nova metodologia de compilação, enquanto permitem que eles reutilizem sua base de código existente. Isso é o que a Apple está tentando fazer com a linguagem de programação Swift, que pode coexistir com o Objective-C (já que usa o mesmo ambiente de tempo de execução).

  2. Em segundo lugar, as próprias plataformas de software subjacentes devem ser reescritas ao longo do tempo usando esses princípios, ao mesmo tempo em que redesenha a hierarquia do módulo no processo para torná-las menos monolíticas. Esta é, naturalmente, uma tarefa gigantesca e pode muito bem levar a melhor parte de uma década (se eles forem corajosos o suficiente para realmente fazê-lo - o que eu não tenho certeza no caso do Google).

Em qualquer caso, é a plataforma que impulsiona a adoção da linguagem e não o contrário.

Referências:

[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf , página 6: "O compilador compila-se em cerca de 3 segundos". Esta cotação é para uma placa de desenvolvimento Xilinx Spartan-3 FPGA de baixo custo com freqüência de 25 MHz e com 1 MByte de memória principal. A partir daqui pode-se facilmente extrapolar para "menos de 1 segundo" para um processador moderno rodando a uma frequência de clock acima de 1 GHz e vários GBytes de memória principal (ou seja, várias ordens de grandeza mais poderosas que a placa FPGA Xilinx Spartan-3) mesmo considerando as velocidades de E / S. Já em 1990, quando Oberon foi executado em um processador NS32X32 de 25MHz com 2-4 MBytes de memória principal, o compilador compilou-se em apenas alguns segundos. A noção de realmente esperar que o compilador conclua um ciclo de compilação era completamente desconhecida para os programadores da Oberon naquela época. Para programas típicos, sempre demorava mais tempo para remover o dedo do botão do mouse que acionava o comando de compilação do que esperar que o compilador completasse a compilação que acabou de ser disparada. Foi realmente uma gratificação instantânea, com tempos de espera próximos de zero. E a qualidade do código produzido, embora nem sempre completamente a par dos melhores compiladores disponíveis naquela época, era notavelmente boa para a maioria das tarefas e bastante aceitável em geral.


Answer #7

Análise de Dependência.

A partir do Go FAQ :

O Go fornece um modelo para a construção de software que facilita a análise de dependência e evita grande parte da sobrecarga do estilo C, incluindo arquivos e bibliotecas.

Essa é a principal razão da rápida compilação. E isso é por design.


Answer #8

A eficiência da compilação foi um dos principais objetivos do design:

Finalmente, pretende-se que seja rápido: deve demorar, no máximo, alguns segundos para construir um grande executável num único computador. Para atender a essas metas, é necessário abordar uma série de questões lingüísticas: um sistema de tipo expressivo, mas leve; simultaneidade e coleta de lixo; especificação de dependência rígida; e assim por diante. FAQ

A FAQ do idioma é bastante interessante no que diz respeito às características específicas do idioma relacionadas à análise:

Segundo, a linguagem foi projetada para ser fácil de analisar e pode ser analisada sem uma tabela de símbolos.





go