Cumpri um programa hello world Go que gerou um executável nativo na minha máquina Linux. Mas fiquei surpreso ao ver o tamanho do simples programa Hello world Go, era de 1,9 MB!
Por que o executável de um programa tão simples em Go é tão grande?
Cumpri um programa hello world Go que gerou um executável nativo na minha máquina Linux. Mas fiquei surpreso ao ver o tamanho do simples programa Hello world Go, era de 1,9 MB!
Por que o executável de um programa tão simples em Go é tão grande?
dotnet publish -r win-x64 -p:publishsinglefile=true -p:publishreadytorun=true -p:publishtrimmed=truegera um arquivo binário de aproximadamente 26 MB!
Respostas:
Esta pergunta exata aparece no FAQ oficial: Por que meu programa trivial é um binário tão grande?
Citando a resposta:
Os ligantes na cadeia ferramenta GC (
5l,6l, e8l) fazer a ligação estática. Todos os binários Go, portanto, incluem o tempo de execução Go, junto com as informações de tipo de tempo de execução necessárias para dar suporte a verificações de tipo dinâmico, reflexão e até mesmo rastreamentos de pilha em tempo de pânico.Um programa simples em C "hello, world" compilado e vinculado estaticamente usando gcc no Linux tem cerca de 750 kB, incluindo uma implementação de
printf. Um programa Go equivalente usandofmt.Printftem cerca de 1,9 MB, mas inclui suporte de tempo de execução mais poderoso e informações de tipo.
Portanto, o executável nativo do Hello World é de 1,9 MB porque contém um tempo de execução que fornece coleta de lixo, reflexão e muitos outros recursos (que seu programa pode não usar, mas está lá). E a implementação do fmtpacote que você usou para imprimir o "Hello World"texto (mais suas dependências).
Agora tente o seguinte: adicione outra fmt.Println("Hello World! Again")linha ao seu programa e compile-o novamente. O resultado não será 2x 1,9 MB, mas apenas 1,9 MB! Sim, porque todas as bibliotecas usadas ( fmte suas dependências) e o runtime já estão adicionados ao executável (e assim apenas mais alguns bytes serão adicionados para imprimir o segundo texto que você acabou de adicionar).
Considere o seguinte programa:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
Se eu construir isso em minha máquina Linux AMD64 (Go 1.9), assim:
$ go build
$ ls -la helloworld
-rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
Recebo um binário com cerca de 2 Mb de tamanho.
A razão para isso (que foi explicada em outras respostas) é que estamos usando o pacote "fmt", que é bastante grande, mas o binário também não foi removido e isso significa que a tabela de símbolos ainda está lá. Se, em vez disso, instruirmos o compilador para remover o binário, ele se tornará muito menor:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
No entanto, se reescrevermos o programa para usar a função embutida print, em vez de fmt.Println, assim:
package main
func main() {
print("Hello World!\n")
}
E então compilar:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
Acabamos com um binário ainda menor. Isso é tão pequeno quanto podemos obter sem recorrer a truques como empacotamento UPX, então a sobrecarga do tempo de execução Go é de aproximadamente 700 Kb.
Observe que o problema de tamanho binário é rastreado pelo problema 6853 no projeto golang / go .
Por exemplo, o commit a26c01a (para Go 1.4) reduz o hello world em 70kB :
porque não escrevemos esses nomes na tabela de símbolos.
Considerando que o compilador, o assembler, o vinculador e o tempo de execução do 1.5 estarão inteiramente em Go, você pode esperar mais otimização.
Atualização 2016 Go 1.7: isso foi otimizado: consulte " Binários menores do Go 1.7 ".
Mas hoje (abril de 2019), o que ocupa mais lugar é runtime.pclntab.
Consulte " Por que meus arquivos executáveis Go são tão grandes? Visualização do tamanho dos executáveis Go usando D3 " de Raphael 'kena' Poss .
Não está muito bem documentado, no entanto, este comentário do código-fonte Go sugere seu propósito:
// A LineTable is a data structure mapping program counters to line numbers.O objetivo desta estrutura de dados é permitir que o sistema de tempo de execução Go produza rastreamentos de pilha descritivos em uma falha ou em solicitações internas por meio do
runtime.GetStackAPI.Portanto, parece útil. Mas por que é tão grande?
O URL https://golang.org/s/go12symtab oculto no arquivo de origem anteriormente vinculado redireciona para um documento que explica o que aconteceu entre Go 1.0 e 1.2. Parafrasear:
antes do 1.2, o vinculador Go emitia uma tabela de linha compactada e o programa a descompactava na inicialização em tempo de execução.
no Go 1.2, foi tomada a decisão de pré-expandir a tabela de linha no arquivo executável em seu formato final adequado para uso direto em tempo de execução, sem uma etapa de descompressão adicional.
Em outras palavras, a equipe de Go decidiu tornar os arquivos executáveis maiores para economizar tempo de inicialização.
Além disso, olhando para a estrutura de dados, parece que seu tamanho geral em binários compilados é superlinear no número de funções no programa, além do tamanho de cada função.