Go で hello-world の減量
Go言語ってビルドサイズが大きいですよね。
バイナリサイズを削減する ldflags
や upx
について、どの程度の減量が見込めるのか確認してみます。
ついでに標準ライブラリへの理解を兼ねて hello-world の書き方もいくつかのパターンで比べてみます。
% go version go version go1.15.5 darwin/amd64
fmt
一般的な hello-world の書き方です。
func main() { fmt.Print("Hello, world!") }
いつも使っている表現ですが fmt
パッケージで標準出力するのにちょっと違和感を持ってました。
os
func main() { os.Stdout.WriteString("Hello, world!") }
fmt
に比べて、こちらの方が標準出力している感がありますね。
でも fmt
の方が色んな型を渡せて便利なので fmt
を使っちゃいますね。
syscall
func main() { syscall.Write(syscall.Stdout, []byte("Hello, world!")) }
使ったことはありませんでしたが、意外とシンプルに書けました。
syscall.Stdout
は uintptr
型の変数で os.File
型の os.Stdout
とは別物です。
バイナリサイズ比較
ビルドしてサイズを比べてみます。
- ldflags を指定して
go build -ldflags="-s -w"
するパターン - ビルドしたバイナリに
upx -9
を適用したパターン - その両方を適用したパターン
を比較したところ以下のようになりました。
default | ldflags | upx | ldflags+upx | |
---|---|---|---|---|
fmt |
2.0M | 1.6M | 1.1M | 620K |
os |
1.5M | 1.1M | 840K | 448K |
syscall |
1.2M | 915K | 708K | 376K |
syscall
が一番小さくなりましたが、それでも何も工夫しなければ1MBを超えています。
御膳立て部分のコードが大きそうですね。
ldflags と upx はそれぞれバイナリサイズを半分程度まで削減できるみたいです。 両方使うと元の大きさの 1/3 程度の小ささになりました。 まだ何をやっているツールか把握していませんが、upx すごいですね。
本当は各ライブラリの表現を部分的に引っ張ってきてより低級な表現に展開する、みたいなことをしたかったのですが、internal
なパッケージをユーザ側で利用することができず諦めました。
おまけ
ディレクトリを跨いだファイルサイズの比較に tree -h が便利
tree -h
でファイルサイズを表示しつつツリー表示してくれます。
--du
オプションをつけると du
的にフォルダの合計サイズも表示してくれます。
% tree --du -h . ├── [ 303] build.sh ├── [5.3M] fmt │ ├── [2.0M] hello-world │ ├── [1.6M] hello-world.ldflags │ ├── [620K] hello-world.ldflags.upx │ ├── [1.1M] hello-world.upx │ └── [ 176] main.go ├── [3.8M] os │ ├── [1.5M] hello-world │ ├── [1.1M] hello-world.ldflags │ ├── [448K] hello-world.ldflags.upx │ ├── [840K] hello-world.upx │ └── [ 83] main.go └── [3.2M] syscall ├── [1.2M] hello-world ├── [915K] hello-world.ldflags ├── [376K] hello-world.ldflags.upx ├── [708K] hello-world.upx └── [ 104] main.go 12M used in 3 directories, 16 files