Cumplí con un programa Hello World Go que generó un ejecutable nativo en mi máquina Linux. Pero me sorprendió ver el tamaño del sencillo programa Hello world Go, ¡era de 1,9 MB!
¿Por qué el ejecutable de un programa tan simple en Go es tan grande?
Cumplí con un programa Hello World Go que generó un ejecutable nativo en mi máquina Linux. Pero me sorprendió ver el tamaño del sencillo programa Hello world Go, ¡era de 1,9 MB!
¿Por qué el ejecutable de un programa tan simple en Go es tan grande?
dotnet publish -r win-x64 -p:publishsinglefile=true -p:publishreadytorun=true -p:publishtrimmed=true
genera un archivo binario de aproximadamente ~ 26 MB!
Respuestas:
Esta pregunta exacta aparece en las preguntas frecuentes oficiales: ¿Por qué mi programa trivial es un binario tan grande?
Citando la respuesta:
Los enlazadores en la cadena de herramientas gc (
5l
,6l
y8l
) hacen la vinculación estática. Por lo tanto, todos los binarios de Go incluyen el tiempo de ejecución de Go, junto con la información de tipo de tiempo de ejecución necesaria para admitir comprobaciones de tipo dinámico, reflexión e incluso seguimientos de pila en tiempo de pánico.Un programa simple de C "hola, mundo" compilado y enlazado estáticamente usando gcc en Linux tiene alrededor de 750 kB, incluida una implementación de
printf
. El uso de un programa Go equivalentefmt.Printf
es de alrededor de 1,9 MB, pero eso incluye información de tipo y soporte de tiempo de ejecución más potente.
Entonces, el ejecutable nativo de su Hello World es de 1.9 MB porque contiene un tiempo de ejecución que proporciona recolección de basura, reflexión y muchas otras características (que su programa podría no usar realmente, pero está ahí). Y la implementación del fmt
paquete que usaste para imprimir el "Hello World"
texto (más sus dependencias).
Ahora intente lo siguiente: agregue otra fmt.Println("Hello World! Again")
línea a su programa y compílelo nuevamente. El resultado no será 2x 1,9 MB, ¡sino solo 1,9 MB! Sí, porque todas las bibliotecas utilizadas ( fmt
y sus dependencias) y el tiempo de ejecución ya están agregadas al ejecutable (por lo que solo se agregarán unos pocos bytes más para imprimir el segundo texto que acaba de agregar).
Considere el siguiente programa:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
Si construyo esto en mi máquina Linux AMD64 (Go 1.9), así:
$ go build
$ ls -la helloworld
-rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
Obtengo un binario de aproximadamente 2 Mb de tamaño.
La razón de esto (que se ha explicado en otras respuestas) es que estamos usando el paquete "fmt" que es bastante grande, pero el binario tampoco se ha eliminado y esto significa que la tabla de símbolos todavía está allí. Si, en cambio, le indicamos al compilador que elimine el binario, se volverá mucho más pequeño:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
Sin embargo, si reescribimos el programa para usar la función incorporada print, en lugar de fmt.Println, así:
package main
func main() {
print("Hello World!\n")
}
Y luego compílelo:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
Terminamos con un binario aún más pequeño. Esto es lo más pequeño que podemos obtener sin recurrir a trucos como el empaquetado UPX, por lo que la sobrecarga del tiempo de ejecución de Go es de aproximadamente 700 Kb.
Tenga en cuenta que el problema del tamaño binario se rastrea mediante el problema 6853 en el proyecto golang / go .
Por ejemplo, comete a26c01a (para Go 1.4) corte hello world en 70kB :
porque no escribimos esos nombres en la tabla de símbolos.
Teniendo en cuenta que el compilador, el ensamblador, el enlazador y el tiempo de ejecución para 1.5 estarán completamente en Go, puede esperar una mayor optimización.
Actualización 2016 Go 1.7: se ha optimizado: consulte " Binarios de Go 1.7 más pequeños ".
Pero en estos días (abril de 2019), lo que más ocupa es runtime.pclntab
.
Consulte " ¿Por qué mis archivos ejecutables Go son tan grandes? Visualización del tamaño de los ejecutables Go usando D3 " de Raphael 'kena' Poss .
No está muy bien documentado, sin embargo, este comentario del código fuente de Go sugiere su propósito:
// A LineTable is a data structure mapping program counters to line numbers.
El propósito de esta estructura de datos es permitir que el sistema de tiempo de ejecución de Go produzca seguimientos descriptivos de la pila en caso de falla o solicitudes internas a través de la
runtime.GetStack
API.Entonces parece útil. Pero, ¿por qué es tan grande?
La URL https://golang.org/s/go12symtab oculta en el archivo fuente anteriormente vinculado redirige a un documento que explica lo que sucedió entre Go 1.0 y 1.2. Parafrasear:
antes de la versión 1.2, el enlazador Go emitía una tabla de líneas comprimidas y el programa la descomprimía al inicializarse en tiempo de ejecución.
en Go 1.2, se tomó la decisión de expandir previamente la tabla de líneas en el archivo ejecutable en su formato final adecuado para uso directo en tiempo de ejecución, sin un paso de descompresión adicional.
En otras palabras, el equipo de Go decidió agrandar los archivos ejecutables para ahorrar tiempo de inicialización.
Además, al observar la estructura de datos, parece que su tamaño total en binarios compilados es superlineal en el número de funciones en el programa, además del tamaño de cada función.