Pesquisei no Google e vasculhei o site Go, mas não consigo encontrar uma explicação para os extraordinários tempos de construçã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.
performance
compiler-construction
build-process
go
Evan Kroske
fonte
fonte
Respostas:
Análise de dependência.
O FAQ do Go costumava conter a seguinte frase:
Embora a frase não esteja mais nas Perguntas frequentes, este tópico é elaborado na palestra Go no Google , que compara a abordagem de análise de dependência de C / C ++ e Go.
Essa é a principal razão da compilação rápida. E isso é por design.
fonte
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 "hello world" em C ++ requer a compilação de 18k linhas de código, o que representa quase meio megabyte de fontes!
Os compiladores Java e C # são executados em uma VM, o que significa que, antes que eles possam compilar qualquer coisa, o sistema operacional precisa carregar toda a VM e, em seguida, eles precisam ser compilados em JIT do bytecode para o código nativo, o que leva algum tempo.
A velocidade de compilação depende de vários fatores.
Alguns idiomas foram projetados para serem compilados rapidamente. Por exemplo, Pascal foi projetado para ser compilado usando um compilador de passagem única.
Os compiladores em si também podem ser otimizados. Por exemplo, o compilador Turbo Pascal foi escrito em um assembler otimizado à 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.
fonte
Existem várias razões pelas 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, da perspectiva 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 com a velocidade de compilação em mente.
Razão principal : a velocidade rápida de compilação foi uma escolha consciente no compilador Go e também no idioma Go
O compilador Go possui um otimizador mais simples que os compiladores C / C ++
Ao contrário do C ++, o Go não possui modelos e funções embutidas. Isso significa que o Go não precisa executar nenhuma instanciação de modelo ou função.
O compilador Go gera código de montagem de baixo nível mais cedo e o otimizador trabalha no código de montagem, 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.
A vinculação final (5l / 6l / 8l) de um programa Go pode ser mais lenta que a vinculação de um programa C / C ++, porque o compilador Go está passando por todo o código de montagem usado e talvez também esteja executando outras ações extras que o C / C ++ ligantes não estão fazendo
Alguns compiladores C / C ++ (GCC) geram instruções em formato de texto (a serem passados para o assembler), 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
Os compiladores projetados com o objetivo de alta velocidade de compilação, como o Jikes, são rápidos. Em uma CPU de 2 GHz, o Jikes pode compilar mais de 20.000 linhas de código Java por segundo (e o modo de compilação incremental é ainda mais eficiente).
fonte
A eficiência da compilação era um dos principais objetivos do projeto:
As perguntas frequentes sobre o idioma são bastante interessantes em relação aos recursos específicos do idioma relacionados à análise:
fonte
aType
fosse uma referência variável e, posteriormente, na fase de análise semântica, quando descobre que não é para imprimir um erro significativo naquele momento.Embora a maioria das opções acima seja verdadeira, há um ponto muito importante que não foi realmente mencionado: gerenciamento de dependências.
O Go precisa incluir apenas os pacotes que você está importando diretamente (como aqueles que já importaram o que precisam). Isso contrasta fortemente com o C / C ++, onde cada arquivo começa incluindo x cabeçalhos, que incluem os cabeçalhos y etc. Conclusão: A compilação do Go leva tempo linear em relação ao número de pacotes importados, onde o C / C ++ leva tempo exponencial.
fonte
Um bom teste para a eficiência de conversão de um compilador é a autocompilação: quanto tempo leva um determinado compilador para se compilar? 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:
Uma sintaxe claramente definida que é matematicamente sólida, para varredura e análise eficientes.
Uma linguagem com segurança de tipo e estaticamente compilada que usa compilação separada com verificação de dependência e tipo através dos limites do módulo, para evitar a leitura desnecessária de arquivos de cabeçalho e a compilação de outros módulos - em oposição à compilação independente , como em C / C ++, em que nenhuma verificação entre módulos é executada pelo compilador (daí a necessidade de reler todos esses arquivos de cabeçalho repetidas vezes, mesmo para um programa simples de uma linha "olá mundo").
Uma implementação eficiente do compilador (por exemplo, análise de cima para baixo de passagem única e descida recursiva) - que obviamente é bastante auxiliada 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 chegando a idiomas modernos como Go (Google) , Swift (Apple), C # (Microsoft) e vários outros.
Vamos torcer para que em breve essa seja a norma e não a exceção. Para chegar lá, duas coisas precisam acontecer:
Primeiro, os fornecedores de plataformas de software como Google, Microsoft e Apple devem começar incentivando os desenvolvedores de aplicativos a usar a nova metodologia de compilação, permitindo que eles reutilizem sua base de códigos existente. É isso que a Apple agora está tentando fazer com a linguagem de programação Swift, que pode coexistir com o Objective-C (já que ele usa o mesmo ambiente de tempo de execução).
Segundo, as próprias plataformas de software subjacentes devem ser reescritas ao longo do tempo usando esses princípios, ao mesmo tempo em que redesenham a hierarquia do módulo no processo para torná-las menos monolíticas. Obviamente, essa é uma tarefa gigantesca e pode levar a maior parte de uma década (se tiverem coragem suficiente para fazê-la - o que não tenho certeza no caso do Google).
De qualquer forma, é a plataforma que impulsiona a adoção do idioma, 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 se compila em aproximadamente 3 segundos". Esta citação é para uma placa de desenvolvimento Xilinx Spartan-3 FPGA de baixo custo, funcionando com uma frequência de clock de 25 MHz e apresentando 1 MByte de memória principal. Com isso, pode- se extrapolar facilmente para "menos de 1 segundo" para um processador moderno executando em uma freqüência de clock bem acima de 1 GHz e vários GBytes de memória principal (ou seja, várias ordens de magnitude mais poderosas que a placa FPil Xilinx Spartan-3), mesmo ao levar em consideração as velocidades de E / S. Já em 1990, quando o Oberon era executado em um processador NS32X32 de 25MHz com 2-4 MBytes de memória principal, o compilador se compilou em apenas alguns segundos. A noção de realmente esperarpara o compilador terminar um ciclo de compilação era completamente desconhecido para os programadores de Oberon naquela época. Para programas típicos, sempre levava mais tempo para remover o dedo do botão do mouse que acionava o comando de compilação do que aguardar o compilador concluir a compilação que acabou de ser acionada. Foi uma gratificação verdadeiramente instantânea, com tempos de espera próximos de zero. E a qualidade do código produzido, apesar de nem sempre estar totalmente a par dos melhores compiladores disponíveis na época, era notavelmente boa para a maioria das tarefas e bastante aceitável em geral.
fonte
O Go foi projetado para ser rápido, e isso mostra.
Observe que o GO não é o único idioma com esses recursos (os módulos são a norma nos idiomas modernos), mas eles o fizeram bem.
fonte
Citando o livro " The Go Programming Language ", de Alan Donovan e Brian Kernighan:
fonte
A idéia básica da compilação é realmente muito simples. Um analisador de descida recursiva, em princípio, pode executar na velocidade de ligação de E / S. A geração de código é basicamente um processo muito simples. Uma tabela de símbolos e um sistema de tipos básicos não é algo que requer muita computação.
No entanto, não é difícil diminuir a velocidade de um compilador.
Se houver uma fase de pré-processador, com diretivas de inclusão em vários níveis , definições de macro e compilação condicional, por mais úteis que sejam essas coisas, não é difícil carregá-lo. (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 para quanto processamento pode ser adicionado a essa fase.
fonte
Simplesmente (em minhas próprias palavras), porque a sintaxe é muito fácil (analisar e analisar)
Por exemplo, nenhuma herança de tipo significa, nenhuma análise problemática para descobrir se o novo tipo segue as regras impostas pelo tipo de base.
Por exemplo, neste exemplo de código: "interfaces", o compilador não vai e verifica se o tipo pretendido implementa a interface fornecida ao analisar esse tipo. Somente até que seja usado (e SE for usado) a verificação será realizada.
Outro exemplo, o compilador informa se você está declarando uma variável e não a está usando (ou se você deve manter um valor de retorno e não está)
O seguinte não é compilado:
Esse tipo de aplicação e princípios tornam o código resultante mais seguro, e o compilador não precisa executar validações extras que o programador pode fazer.
No geral, todos esses detalhes facilitam a análise de um idioma, o que resulta em compilações rápidas.
Mais uma vez, em minhas próprias palavras.
fonte
Eu acho que o Go foi projetado em paralelo com a criação do compilador, então eles eram melhores amigos desde o nascimento. (IMO)
fonte
O quê mais?
fonte