Wie kann ich die Größe kompilierter Dateien reduzieren?

81

Vergleichen wir c und gehen: Hello_world.c:

#include<stdio.h>
int main(){
    printf("Hello world!");
}

Hello_world.go:

package main
import "fmt"
func main(){
    fmt.Printf("Hello world!")
}

Kompilieren Sie beide:

$gcc Hello_world.c -o Hello_c 
$8g Hello_world.go -o Hello_go.8
$8l Hello_go.8 -o Hello_go

und was ist das?

$ls -ls
... 5,4K 2010-10-05 11:09 Hello_c
... 991K 2010-10-05 11:17 Hello_go

Über 1 MB Hallo Welt. Willst du mich verarschen? Was ich falsch mache?

(Strip Hello_go -> nur 893K)

zyrg
quelle
2
Auf einem x86_64-Mac beträgt die "Hello World" -Binärdatei 1,3 MB, wie auf einem x64-Linux-Computer, den ich annehme. Im Gegensatz dazu ist die ARM x32-Binärdatei genauso groß wie die x86_32-Binärdatei. Die Größe hängt wesentlich von der "Wort" -Länge der jeweiligen Architektur ab. Auf x32-Computern ist es 32 Bit, auf x64 64 Bit breit. Daher ist die x32-Binärdatei "Hello World" etwa 30% kleiner.
Alex
17
@ Nick: Angesichts der Tatsache, dass GO als Systemsprache vermarktet wird, halte ich es für eine faire Frage. Ich arbeite in Systemen und wir haben nicht immer den Luxus von 4 GB + RAM und einer riesigen Festplatte.
Ed S.
3
Eine ausführbare Datei mit 893 KB ist weit entfernt von "4 GB + RAM und einer riesigen Festplatte", und wie andere bereits betont haben, schließt dies die statisch verknüpfte Go-Laufzeit ein, die leicht ausgeschlossen werden kann.
Nick Johnson
22
Ja, es ist weit entfernt, und ich kenne die Antwort, aber es ist eine berechtigte Frage, und die Einstellung "Wer kümmert sich um den Speicherverbrauch?" Kommt in der Regel von der Arbeit an Systemen, auf denen es keine Rolle spielt. Sie scheinen diese Unwissenheit zu denken ist in Ordnung und es ist besser, einfach keine Fragen zu stellen. Und ich werde es noch einmal sagen: Manchmal ist ~ 1 MB eine Menge, Sie arbeiten offensichtlich nicht in dieser Welt. BEARBEITEN - Sie arbeiten bei Google! lol. Ich verstehe das immer noch nicht "Wen interessiert das?" - Einstellung
Ed S.
12
Offensichtlich in der Java-Abteilung;)
Matt Joiner

Antworten:

28

Ist es ein Problem, dass die Datei größer ist? Ich kenne Go nicht, aber ich würde annehmen, dass es statisch eine Laufzeitbibliothek verknüpft, was für das C-Programm nicht der Fall ist. Aber wahrscheinlich ist das kein Grund zur Sorge, sobald Ihr Programm größer wird.

Wie hier beschrieben , ist die statische Verknüpfung der Go-Laufzeit die Standardeinstellung. Auf dieser Seite erfahren Sie auch, wie Sie die dynamische Verknüpfung einrichten.

Dirk Vollmar
quelle
2
Es gibt ein offenes Problem , mit dem Meilenstein für Go1.5
Joe
80

Wenn Sie ein Unix-basiertes System (z. B. Linux oder Mac OSX) verwenden, können Sie versuchen, die in der ausführbaren Datei enthaltenen Debugging-Informationen zu entfernen, indem Sie sie mit dem Flag -w erstellen:

go build -ldflags "-w" prog.go

Die Dateigrößen werden drastisch reduziert.

Weitere Informationen finden Sie auf der GDB-Seite: http://golang.org/doc/gdb

Amged Rustom
quelle
3
Das gleiche mit erreicht stripBefehl: go build prog.go; strip progdann haben wir so genannten Streifen ausführbare Datei: ELF 64-Bit - LSB ausführbare Datei, x86-64, Version 1 (SYSV), dynamisch gelinkt (verwendet Libs gemeinsam), gestrippt
Alexander I.Grafov
1
Dies funktioniert unter Windows und bietet eine etwas kleinere Binärdatei als die, die durch normales Erstellen und Entfernen erstellt wird.
Isxek
9
@Axel: Das Entfernen von Go-Binärdateien wird ausdrücklich nicht unterstützt und kann in einigen Fällen schwerwiegende Fehler verursachen. Siehe hier .
Flimzy
9
Derzeit sind die Dokumentationslisten -wanstelle von -s. Ich
bin
1
@ Dynom: -w scheint die Größe etwas zu reduzieren, aber die Reduzierung ist nicht so gut wie mit -s. -s scheint zu implizieren -w: golang.org/cmd/link
arkod
41

Die Antwort 2016:

1. Verwenden Sie Go 1.7

2. Kompilieren Sie mit go build -ldflags "-s -w"

 ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 976K May 26 20:49 hello*

3. Dann verwenden upx, goupxwird seit 1.6 nicht mehr benötigt.

 ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 367K May 26 20:49 hello*
OneOfOne
quelle
3
Ich möchte hier hinzufügen, dass hier wahrscheinlich die üblichen Vorbehalte gegen UPX gelten. Weitere Informationen finden Sie hier: stackoverflow.com/questions/353634/… (ein Großteil davon gilt auch für Nicht-Windows-Betriebssysteme).
Jonas
23

Go-Binärdateien sind groß, da sie statisch verknüpft sind (mit Ausnahme von Bibliotheksbindungen, die cgo verwenden). Wenn Sie versuchen, ein C-Programm statisch zu verknüpfen, wird es auf eine vergleichbare Größe anwachsen.

Wenn dies wirklich ein Problem für Sie ist (was ich kaum glauben kann), können Sie mit gccgo kompilieren und dynamisch verknüpfen.

Evan Shaw
quelle
19
Es ist auch eine nette Bewegung für eine Sprache, die noch nicht allgemein anerkannt ist. Niemand ist daran interessiert, seine Systeme mit go system libs zu überladen.
Matt Joiner
4
Die Go-Ersteller bevorzugen ausdrücklich die dynamische Verknüpfung: schädlich.cat-v.org / software/ dynamic-linking . Google verknüpft statisch alle seine C und C ++: reddit.com/r/golang/comments/tqudb/…
Graham King
13
Ich denke, dass der verlinkte Artikel das Gegenteil besagt - Go-Ersteller bevorzugen ausdrücklich statische Verknüpfungen.
Petar Donchev
17

Sie sollten goupx bekommen , es wird Golang ELF ausführbare Dateien "reparieren", um damit zu arbeiten upx. In einigen Fällen ist die Dateigröße bereits um 78% kleiner geworden ~16MB >> ~3MB.

Das Kompressionsverhältnis liegt normalerweise bei 25%, daher ist es einen Versuch wert:

$ go get github.com/pwaller/goupx
$ go build -o filename
$ goupx filename

>>

2014/12/25 10:10:54 File fixed!

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  16271132 ->   3647116   22.41%  linux/ElfAMD   filename                            

Packed 1 file.

EXTRA: -sFlag (Streifen) kann die Bin-Datei noch weiter verkleinerngoupx -s filename

Marcio
quelle
1
Das ist ausgezeichnet, danke! Ich habe GOARCH = 386 gesetzt und benutze jetzt schon eine Weile normales Upx.
Codekoala
9
Dies wird ab Go 1.6 nicht mehr benötigt, Sie können den normalen Upx verwenden.
OneOfOne
12

Erstellen Sie eine Datei mit dem Namen main.go, versuchen wir es mit einem einfachen Hallo-Welt-Programm.

package main

import "fmt"

func main(){
    fmt.Println("Hello World!")
}

Ich benutze go Version 1.9.1

$ go version
 go version go1.9.1 linux/amd64

Kompilieren Sie mit dem Standardbefehl go build.

$ go build main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.8M Oct 27 07:47 main

Lassen Sie uns noch einmal mit kompilieren, go buildaber mit ldflagswie oben vorgeschlagen,

$ go build -ldflags "-s -w" main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.2M Oct 27 08:15 main

Die Dateigröße wird um 30% reduziert.

Lassen Sie uns jetzt verwenden gccgo,

$ go version
 go version go1.8.1 gccgo (GCC) 7.2.0 linux/amd64

Bauen gehen mit gccgo,

$ go build main.go
$ ls -lh
-rwxr-xr-x 1 nil nil 34K Oct 27 12:18 main

Die Binärgröße wird um fast 100% reduziert. Lassen Sie uns noch einmal versuchen, unsere main.gomit gccgoaber mit Build-Flags zu bauen.

$ go build -gccgoflags "-s -w" main.go
-rwxr-xr-x 1 nil nil 23K Oct 27 13:02 main

Warnung: Da gccgoBinärdateien dynamisch verknüpft wurden. Wenn Sie eine Binärdatei haben, die sehr groß ist, wird Ihre Binärdatei beim Kompilieren mit gccgo nicht um 100% verringert, sondern um einen beträchtlichen Betrag.

Im Vergleich zu gc kompiliert gccgo Code langsamer, unterstützt jedoch leistungsfähigere Optimierungen, sodass ein von gccgo erstelltes CPU-gebundenes Programm normalerweise schneller ausgeführt wird. Alle im Laufe der Jahre in GCC implementierten Optimierungen sind verfügbar, einschließlich Inlining, Schleifenoptimierungen, Vektorisierung, Befehlsplanung und mehr. Während es nicht immer besseren Code erzeugt, können mit gccgo kompilierte Programme in einigen Fällen 30% schneller ausgeführt werden.

Die GCC 7-Versionen werden voraussichtlich eine vollständige Implementierung der Go 1.8-Benutzerbibliotheken enthalten. Wie bei früheren Versionen ist die Go 1.8-Laufzeit nicht vollständig zusammengeführt, dies sollte jedoch für Go-Programme nicht sichtbar sein.

Vorteile:

  1. Reduzierte Größe
  2. Optimiert.

Nachteile

  1. Langsam
  2. Die neueste Version von kann nicht verwendet werden go.

Sie können hier und hier sehen .

Nilsocket
quelle
Danke dir. Ich habe eine Frage, während ich lese, gogccunterstützt ARM-Prozessor nicht, ist das richtig? Und können Sie ein Tool vorschlagen, das die Größe der Golang-Build-Datei reduziert?
M. Rostami
Wie kompilierst du mit gccgo?
Vitaly Zdanevich
10

Kompakteres Hallo-Welt-Beispiel:

package main

func main() {
  print("Hello world!")
}

Wir sind große fmtPaket übersprungen und merklich reduziert binär:

  $ go build hello.go
  $ ls -lh hello
  ... 259K ... hello2
  $ strip hello
  $ ls -lh hello
  ... 162K ... hello2

Nicht so kompakt wie C, aber obwohl in K nicht M gemessen :) Ok, es ist kein allgemeiner Weg, der nur einige Möglichkeiten zur Optimierung einer Größe zeigt: Verwenden Sie einen Streifen und versuchen Sie, minimale Pakete zu verwenden. Wie auch immer, Go ist keine Sprache, um winzige Binärdateien zu erstellen.

Alexander I.Grafov
quelle
11
Dieser Code verwendet die integrierte Funktion print (), und diese Vorgehensweise wird nicht empfohlen. Und die Frage war nicht, wie die Codegröße des bereitgestellten Beispielprogramms reduziert werden kann, sondern allgemeiner.
wldsvc
@wldsvc Ich stimme dir zu. Obwohl Sie nicht verstehen, warum print () ein schlechter Weg für die Anzeigeausgabe ist? Für einfache Skripte oder für die Debug-Ausgabe ist es gut genug.
Alexander I. Grafov
3
Siehe doc.golang.org/ref/spec#Bootstrapping. Diese Funktionen sind der Vollständigkeit halber dokumentiert, es wird jedoch nicht garantiert, dass sie in der Sprache bleiben . Für mich bedeutet dies, dass Sie sie in keinem Skript verwenden, außer für einmalige Debugging-Zwecke.
wldsvc
3

Antwort 2018 für den nächsten Go 1.11, wie von Brad Fitzpatrick (Mitte Juni 2018) getwittert :

Seit einigen Minuten sind DWARF-Abschnitte in # Golang ELF-Binärdateien jetzt komprimiert, sodass die Binärdateien an der Spitze jetzt kleiner als Go 1.10 sind, selbst wenn alle zusätzlichen Debug-Elemente an der Spitze vorhanden sind.

https://pbs.twimg.com/media/DfwkBaqUEAAb8-Q.jpg:large

Vgl. Golang Ausgabe 11799 :

Das Komprimieren unserer Debug-Informationen kann zu einem erheblichen Gewinn bei der Dateigröße führen.

Weitere Informationen finden Sie in Commit 594eae5

cmd / link: Komprimiert DWARF-Abschnitte in ELF-Binärdateien

Der schwierigste Teil davon ist, dass der binäre Layoutcode (blk, elfshbits und verschiedene andere Dinge) einen konstanten Versatz zwischen den Speicherorten der Symbole und Abschnitte und ihren virtuellen Adressen annimmt.

Die Komprimierung unterbricht natürlich diesen konstanten Versatz.
Wir müssen jedoch vor der Komprimierung allen virtuellen Adressen zuweisen, um Verschiebungen vor der Komprimierung aufzulösen.

Infolgedessen muss die Komprimierung die "Adresse" der DWARF-Abschnitte und -Symbole basierend auf ihrer komprimierten Größe neu berechnen.
Glücklicherweise befinden sich diese am Ende der Datei, sodass keine anderen Abschnitte oder Symbole gestört werden. (Und es gibt natürlich eine überraschende Menge an Code, die davon ausgeht, dass das DWARF-Segment an letzter Stelle steht. Was ist also noch ein Ort?)

name        old exe-bytes   new exe-bytes   delta
HelloSize      1.60MB ± 0%     1.05MB ± 0%  -34.39%  (p=0.000 n=30+30)
CmdGoSize      16.5MB ± 0%     11.3MB ± 0%  -31.76%  (p=0.000 n=30+30)
[Geo mean]     5.14MB          3.44MB       -33.08%

Rob Pike erwähnt :

Das hilft nur auf Maschinen, die ELF verwenden.
Binärdateien sind immer noch zu groß und wachsen.

Brad antwortete:

Zumindest ist es etwas. War im Begriff, viel schlimmer zu werden.
Stoppte die Blutung für eine Freisetzung.

Gründe : Debuggen von Informationen, aber auch Registrieren der Karte für den GC, damit jede Anweisung ein Sicherungspunkt ist.

VonC
quelle
1

Die Binärdatei enthält standardmäßig den Garbage Collector, das Schedulding-System, das die Go-Routinen verwaltet, und alle von Ihnen importierten Bibliotheken.

Das Ergebnis ist eine minimale Größe von ca. 1 MB.

JulienFr
quelle
1

Ab Go 1.8 können Sie das neue Plugin-System auch verwenden, um Ihre Binärdatei in etwas aufzuteilen, das gemeinsam genutzten Bibliotheken ähnelt. In dieser Version funktioniert es nur unter Linux, aber andere Plattformen werden wahrscheinlich in Zukunft unterstützt.

https://tip.golang.org/pkg/plugin/

Joppe
quelle
1

Standardmäßig verknüpft gcc dynamisch und statisch.

Wenn Sie Ihren C-Code jedoch statisch verknüpfen, erhalten Sie möglicherweise eine Binärdatei mit einer größeren Größe.

In meinem Fall:

  • go x64 (1.10.3) - generierte Binärdatei mit einer Größe von 1214208 Byte
  • gcc x64 (6.2.0) - generierte Binärdatei mit einer Größe von 1421312 Byte

Beide Binärdateien sind statisch verknüpft und ohne debug_info.

go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
Vitaly
quelle
0

Sie können einen Blick auf meine kleine Recherche zu diesem Thema werfen: https://github.com/xaionaro/documentation/blob/master/golang/reduce-binary-size.md

Es wird Schritt für Schritt gezeigt, wie ein statisch-binäres 2-MB-Hallo-Welt-Beispiel auf 15-KB-statisch-binär oder auf 10-KB-dynamisch-binär reduziert wird. Natürlich gibt es viele Einschränkungen.

Dmitrii Okunev
quelle