Edited at

Go のバイナリには -ldflags '-w -s' でコンパイルしてもたくさんパスが埋め込まれていた

More than 1 year has passed since last update.

Go 使いの人たちには「なにをいまさら」という内容かもしれない。

Go のバイナリをビルドする際に

$ go build -ldflags '-w -s'

とコンパイルすることで、 DWARF とシンボルテーブルの情報をバイナリに残さずコンパイルできる。

しかし、自分は気になったので strings コマンドを生成後のバイナリにかけてみた。

するとバイナリには

_/Users/kitsuyui/.go/*****/*****.go

_/Users/kitsuyui/develop/*****/*****.go

というような行が、まだたくさん残っていた。

どうやら DWARF やシンボルテーブルの情報を削除しても、コンパイルの際のパスなどの情報は残ってしまうようだ。


消してみると 10% くらい節約できた

そこで、全て浅いディレクトリでコンパイルしなおしたところ、約 5MB のバイナリが約 4.5MB ほどまで節約できた。

ソースコードを細分化していればしているほど、モジュールを使っていれば使っているほど、含むパスは多くなるので、これらにより節約できるサイズは大きくなる。

Go の単一バイナリを Docker コンテナにするような状況では、 10% の節約は心理的に大きい。 (嬉しい)


使い所


  • 早すぎる最適化ゲームをしたいとき (ただし -ldflags '-w -s' ほどではない)

  • 自分の開発環境のパスが配布用のバイナリに含まれるのは生理的に嫌だ、というとき


でも毎回ディレクトリを配置しなおすの面倒だよね

Docker を使おう。

tcnksm/gox という便利な Docker イメージがあるので、これを自分は使っている。

$ docker run --rm -v "$(pwd)":/foobar -w /foobar tcnksm/gox:latest \

sh -c "go get -d ./... && gox -ldflags '-w -s'"

こんな風にすれば簡単に複数の環境をターゲットに、浅いパスでコンパイルできる。

go get は場合によってネットワーク負荷が大きい、時間がかかる処理になりがちなので、ここだけ Dockerfile を書いておいたほうがよいかもしれない。


補足

この記事を書くにあたって使用した Go のバージョンは 1.7.3