コンパイルされたファイルサイズを減らす方法は?


82

cを比較してみましょう:Hello_world.c:

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

Hello_world.go:

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

両方をコンパイルします。

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

で、それ何?

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

約1MbHelloworld。私をからかってるの?私は何が間違っていますか?

(Hello_goを削除-> 893Kのみ)


2
x86_64 Macでは、「HelloWorld」バイナリはx64Linuxマシンと同じように1.3MBです。対照的に、ARMx32バイナリはx86_32バイナリと同じ大きさです。サイズは、それぞれのアーキテクチャの「ワード」の長さに大きく依存します。x32マシンでは32ビット、x64では64ビット幅です。したがって、x32の「HelloWorld」バイナリは約30%小さくなります。
アレックス

17
@Nick:GOがシステム言語として販売されていることを考えると、それは公正な質問だと思います。私はシステムで働いていますが、4GB以上のRAMと巨大なディスクの贅沢を常に持っているわけではありません。
Ed S.

3
893kbの実行可能ファイルは、「4GB以上のRAMと巨大なディスク」とはかけ離れています。他の人がすでに指摘しているように、これには静的にリンクされたgoランタイムが含まれます。これは簡単に除外できます。
ニックジョンソン

22
はい、それは遠い叫びです、そして私は答えを知っています、しかしそれは有効な質問であり、「メモリ消費を気にする人」の態度はそれが問題ではないシステムで働くことから来る傾向があります。あなたは無知だと思うようです大丈夫です。質問しないほうがいいです。もう一度言いますが、1 MBが多い場合もありますが、明らかにその世界では機能しません。編集-Googleで作業しています!笑。まだ取得できません。 「誰が心配態度けれども。
エドS.

12
明らかにJava部門で;)
Matt Joiner

回答:


29

ファイルが大きいのは問題ですか?Goはわかりませんが、Cプログラムには当てはまらないランタイムライブラリを静的にリンクしていると思います。しかし、プログラムが大きくなるとすぐに、おそらくそれは心配する必要はありません。

ここで説明するように、Goランタイムを静的にリンクすることがデフォルトです。このページでは、ダイナミックリンクを設定する方法についても説明しています。


2
Go1.5のマイルストーンが設定された未解決の問題があります
Joe

80

Unixベースのシステム(LinuxやMac OSXなど)を使用している場合は、-wフラグを使用して実行可能ファイルをビルドすることにより、実行可能ファイルに含まれているデバッグ情報を削除してみてください。

go build -ldflags "-w" prog.go

ファイルサイズが大幅に削減されます。

詳細については、GDBのページをご覧ください:http//golang.org/doc/gdb


3
同じことがstripコマンドでも実現されます。go build prog.go; strip progいわゆるストライプ実行可能ファイル:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク(共有ライブラリを使用)、ストリップ
Alexander I.Grafov 2014

1
これはWindowsで機能し、通常のビルドとストリッピングによって生成されたものと比較して、わずかに小さいバイナリファイルを提供します。
isxek 2014年

9
@Axel:goバイナリの削除は明示的にサポートされておらず、場合によっては重大なバグを引き起こす可能性があります。こちらをご覧ください
Flimzy 2014年

9
現在、ドキュメントには-w、の代わりにがリストされています-s。違いは
わかり

1
@Dynom:-wはサイズをいくらか縮小するようですが、縮小は-sの場合ほど良くありません。-sは-wを意味するようです:golang.org/cmd/link
arkod

42

2016年の回答:

1. Go1.7を使用します

2.コンパイルする go build -ldflags "-s -w"

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

3.次に使用はupxgoupxもはや1.6以降必要ありません。

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

3
ここで、UPXに関する通常の警告がおそらくここに当てはまることを付け加えたいと思います。詳細については、stackoverflow.com / questions / 353634 / を参照してください(その多くは、Windows以外のオペレーティングシステムにも適用されます)。
Jonas

23

Goバイナリは静的にリンクされているため、大きくなります(cgoを使用したライブラリバインディングを除く)。Cプログラムを静的にリンクしてみると、同等のサイズに成長することがわかります。

これが本当にあなたにとって問題である場合(私は信じがたいです)、gccgoでコンパイルして動的にリンクすることができます。


19
また、まだ広く採用されていない言語にとっては、いい動きです。goシステムライブラリでシステムを乱雑にすることに誰も興味がありません。
マットジョイナー2010年

4
Goの作成者は、動的リンクを明示的に好みます:dusant.cat-v.org/software/dynamic-linking。GoogleはすべてのCとC ++を静的にリンクしています:reddit.com/r/golang/comments/tqudb/…–
Graham King

13
リンクされた記事は反対のことを述べていると思います-Goの作成者は明示的に静的リンクを好みます。
Petar Donchev 2015

17

goupxを入手する必要があります。これにより、GolangELF実行可能ファイルが動作するように「修正」されupxます。場合によっては、すでに約78%のファイルサイズの縮小があり~16MB >> ~3MBます。

圧縮率は通常25%になる傾向があるため、試してみる価値があります。

$ 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.

-s追加:フラグ(ストリップ)はbinファイルをさらに縮小できますgoupx -s filename


1
これは素晴らしいです、ありがとう!私はGOARCH = 386を設定していて、しばらくの間通常のupxを使用しています。
codekoala 2015年

9
Go 1.6以降、これは不要になり、通常のupxを使用できます。
OneOfOne 2016年

12

という名前のファイルを作成し、main.go簡単なhelloworldプログラムで試してみましょう。

package main

import "fmt"

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

goバージョン1.9.1を使用しています

$ go version
 go version go1.9.1 linux/amd64

標準go buildコマンドでコンパイルします。

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

でもう一度コンパイルしてみましょうgo buildが、ldflags上記のように、

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

ファイルサイズが30%削減されます。

さて、使用することができますgccgo

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

建物は一緒gccgoに行く、

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

バイナリサイズはほぼ100%削減されます。ビルドフラグを使用してビルドmain.goしてみましょうgccgo

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

警告: としてgccgoバイナリが動的にリンクされていました。サイズが非常に大きいバイナリがある場合、gccgoでコンパイルしたときのバイナリは100%減少しませんが、サイズがかなり減少します。

gcと比較すると、gccgoはコードのコンパイルに時間がかかりますが、より強力な最適化をサポートしているため、gccgoによってビルドされたCPUバウンドプログラムは通常、より高速に実行されます。インライン化、ループ最適化、ベクトル化、命令スケジューリングなど、長年にわたってGCCに実装されたすべての最適化が利用可能です。常に優れたコードが生成されるとは限りませんが、gccgoでコンパイルされたプログラムの実行速度が30%速くなる場合があります。

GCC 7リリースには、Go1.8ユーザーライブラリの完全な実装が含まれる予定です。以前のリリースと同様に、Go 1.8ランタイムは完全にはマージされていませんが、Goプログラムには表示されないはずです。

長所:

  1. サイズを縮小
  2. 最適化。

短所

  1. スロー
  2. の最新バージョンは使用できませんgo

あなたはここここで見ることができます


ありがとうございました。私が読んだように、gogccARMプロセッサをサポートしていないという質問がありますよね?そして、Golangビルドファイルのサイズを減らすツールを提案できますか?
M. Rostami

gccgoでどのようにコンパイルしますか?
Vitaly Zdanevich

10

よりコンパクトなhello-worldの例:

package main

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

大きなfmtパッケージはスキップされ、バイナリが大幅に削減されます。

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

Cほどコンパクトではありませんが、MではなくKで測定されます:)わかりました。一般的な方法ではなく、サイズを最適化するいくつかの方法を示しています。ストリップを使用し、最小限のパッケージを使用してみてください。とにかく、Goは小さなサイズのバイナリを作成するための言語ではありません。


11
このコードは組み込みのprint()関数を使用するため、この方法はお勧めしません。そして問題は、提供されたサンプルプログラムのコードサイズをどのように減らすかではなく、より一般的な方法でした。
wldsvc 2013

@wldsvc私はあなたのメモに同意します。print()が表示出力に悪い方法である理由がわかりませんが?単純なスクリプトまたはデバッグ出力の場合は、それで十分です。
アレクサンドル1世グラフォフ2014

3
doc.golang.org/ref/spec#Bootstrappingを 参照してくださいこれらの関数は完全期すために文書化されていますが、言語にとどまることが保証されていません。私にとっては、1回限りのデバッグ目的を除いて、どのスクリプトでも「それらを使用しない」ことを意味します。
wldsvc 2014

3

ブラッド・フィッツパトリックがツイートした次のGo 1.11に対する2018年の回答(2018年6月中旬):

数分前の時点で、#golang ELFバイナリのDWARFセクションが圧縮されているため、チップに余分なデバッグ機能がすべて含まれていても、チップバイナリはGo1.10よりも小さくなっています。

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

Cf. Golang問題11799

デバッグ情報を圧縮すると、ファイルサイズが大幅に安くなる可能性があります。

コミット594eae5で詳細を参照してください

cmd / link:ELFバイナリのDWARFセクションを圧縮します

これの最も難しい部分は、バイナリレイアウトコード(blk、elfshbits、およびその他のさまざまなもの)が、シンボルとセクションのファイル位置とそれらの仮想アドレスの間の一定のオフセットを想定していることです。

もちろん、圧縮はこの一定のオフセットを壊します。
ただし、圧縮前に再配置を解決するには、圧縮前にすべてに仮想アドレスを割り当てる必要があります。

その結果、圧縮では、圧縮されたサイズに基づいて、DWARFセクションとシンボルの「アドレス」を再計算する必要があります。
幸い、これらはファイルの最後にあるため、他のセクションやシンボルを混乱させることはありません。(もちろん、DWARFセグメントが最後に来ると想定する驚くべき量のコードがあるので、もう1つ場所は何ですか?)

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%

ロブパイクは言及します

これは、ELFを使用するマシンでのみ役立ちます。
バイナリはまだ大きすぎて、成長しています。

ブラッドは答えた:

少なくともそれは何かです。ずっと悪化しそうだった。
1回のリリースで出血を止めました。

理由:デバッグ情報だけでなく、GCのマップを登録して、任意の命令をセーブポイントにします。


1

バイナリには、デフォルトで、ガベージコレクタ、goルーチンを管理するスケジューリングシステム、およびインポートするすべてのライブラリが含まれています。

その結果、最小サイズは約1Mbになります。


1

Go 1.8からは、新しいプラグインシステムを使用して、バイナリを共有ライブラリに似たものに分割することもできます。このリリースでは、Linuxでのみ機能しますが、将来的には他のプラットフォームもサポートされる可能性があります。

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



1

デフォルトでは、gccは動的にリンクし、静的に移動します。

ただし、Cコードを静的にリンクすると、より大きなサイズのバイナリが得られる可能性があります。

私の場合:

  • go x64(1.10.3)-サイズ1214208バイトで生成されたバイナリ
  • gcc x64(6.2.0)-サイズ1421312バイトで生成されたバイナリ

両方のバイナリは静的にリンクされており、debug_infoはありません。

go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.