golangのFAQを見たら面白かったよって話 その1
golang FAQ
golang blog ついでにgolang FAQ見ていた時の話
Why is my trivial program such a large binary?
golangの公式にFAQがあったので眺めていたら以下の質問がありました。
Why is my trivial program such a large binary?
要はhello, worldみたいな小さいコードなのになんでビルドするとでかい実行ファイルになるのかということらしいです。
え?そなの?。。。。CPU使用率とかメモリ量とかしか見てなかったので知らんかった。
というわけで他言語と比較してみました。
・ windows10 home
・ gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
・ javac 12.0.1
・ go version go1.13.8 windows/amd64
以下は各言語のコードと実行したコマンドです。ただのhello, worldなので見なくても問題ないです。
//binary.c #include<stdio.h> int main(){ printf("hello, world\n"); return 0; }
//gcc exec cmd gcc -O3 binary.c
//Binary.java class Binary { public static void main(String[] args) { System.out.println("Hello, world."); } }
//javac exec cmd javac Binary.java
//binary.go package main import "fmt" func main(){ fmt.Print("hello, world\n") }
//go exec cmd go build binary.go
そんでもって生成されたバイナリのサイズが以下のものです。
gcc : 54,058 byte javac : 419 byte go : 2,161,115 byte
比較するとかなりデカかった。
なんでや...
なぜバイナリがでかくなるのか
FAQなのですぐ下に答えは書いてありました。以下はFAQの引用です。
The linker in the gc toolchain creates statically-linked binaries by default. All Go binaries therefore include the Go runtime, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces. A simple C "hello, world" program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf weighs a couple of megabytes, but that includes more powerful run-time support and type and debugging information. ・ ・ ・
つまり、デバッグ情報があるので重くなっているらしい。
-ldflags=-w のオプションをつけるとDWARFの生成がなくなり軽くなるらしい。とりあえずやってみました。-ldflags=-w のオプションをつけただけです。
go build -ldflags=-w .\binary.go
noflag.exe : 2,106,368 byte flag.exe : 1,653,248 byte
確かにだいぶ下がっていますね。
でも、DWAEFとは......?
DWAEFとは
どうやらデバッグ情報の形式らしい。
しかし、私の環境はwindowsなのでPDBという形式になるらしい。
以下のもコマンドでデバッグ環境を入れられるらしいです。
> go get -u github.com/derekparker/delve/cmd/dlv
私の環境では以下のようになりました。
> dlv debug binary.go Type 'help' for list of commands. (dlv) b main.main Breakpoint 1 set at 0x4bb156 for main.main() C:/Users/ttnmr/HOME/tmp/binary.go:5 (dlv) c > main.main() C:/Users/ttnmr/HOME/tmp/binary.go:5 (hits goroutine(1):1 total:1) (PC: 0x4bb156) 1: package main 2: 3: import "fmt" 4: => 5: func main(){ 6: fmt.Print("hello, world\n") 7: } (dlv)
gdbなどと使い方は同じのようです。これでfmt.printなどでデバッグしなくてもスマートにできそう。
参考
golang FAQ
デバッグ情報の歩き方
Microsoft、PDBフォーマットの情報をGitHubで公開
はじめてのgolang デバッグ&テストコード