how golang cross compile check go executable

golang - Motivo del enorme tamaño del ejecutable compilado de Go



golang packages (3)

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 simple programa Hello World Go, ¡era 1.9MB!

¿Por qué es que el ejecutable de un programa tan simple en Go es tan grande?


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 archivo 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 su lugar 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 compilarlo:

$ 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 embalaje UPX, por lo que la sobrecarga del tiempo de ejecución de Go es de aproximadamente 700 Kb.


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 y 8l ) realizan enlaces estáticos. 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 trazas de pila de tiempo de pánico.

Un simple programa C "hola, mundo" compilado y vinculado estáticamente usando gcc en Linux es de alrededor de 750 kB, incluida una implementación de printf . Un programa Go equivalente que usa fmt.Printf es de alrededor de 1.9 MB, pero eso incluye un soporte de tiempo de ejecución más potente e información de tipo.

Entonces, el ejecutable nativo de Hello World es 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 realmente no puede usar, pero está ahí). Y la implementación del paquete fmt que usó para imprimir el texto "Hello World" (más sus dependencias).

Ahora intente lo siguiente: agregue otra línea fmt.Println("Hello World! Again") a su programa y compílelo nuevamente. ¡El resultado no será 2x 1.9MB, sino solo 1.9 MB! Sí, porque todas las bibliotecas utilizadas ( fmt y sus dependencias) y el tiempo de ejecución ya se agregaron al ejecutable (y, por lo tanto, solo se agregarán unos pocos bytes más para imprimir el segundo texto que acaba de agregar).


Tenga en cuenta que el problema de tamaño binario se rastrea por el problema 6853 en el proyecto golang / go .

Por ejemplo, cometer a26c01a (para Go 1.4) cortar 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: esto se ha optimizado: consulte " Binarios más pequeños de Go 1.7 ".

Pero en este día (abril de 2019), lo que más ocupa el lugar 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 Go produzca rastros descriptivos de la pila en caso de bloqueo o de solicitudes internas a través de la API runtime.GetStack .

Entonces parece útil. ¿Pero por qué es tan grande?

La URL https://golang.org/s/go12symtab oculta en el archivo fuente mencionado anteriormente redirige a un documento que explica lo que sucedió entre Go 1.0 y 1.2. Parafrasear:

antes de 1.2, el enlazador Go emitía una tabla de líneas comprimidas, y el programa la descomprimía tras la inicialización 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 a 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ó aumentar el tamaño de los archivos ejecutables para ahorrar tiempo de inicialización.

Además, al observar la estructura de datos, parece que su tamaño general en binarios compilados es superlineal en la cantidad de funciones del programa, además de cuán grande es cada función.