Goブログ
Go 1.7 バイナリサイズの縮小
はじめに
Goはサーバーの記述を目的として設計されました。現在、Goは最も広くサーバー記述に使用されており、その結果、ランタイムとコンパイラに関する多くの作業は、サーバーにとって重要な問題(レイテンシ、展開の容易さ、正確なガベージコレクション、高速な起動時間、パフォーマンス)に焦点を当てています。
Goがより幅広い種類のプログラムに使用されるようになるにつれて、考慮すべき新しい問題が出てきました。その1つがバイナリサイズです。これは長年懸念事項でしたが(issue #6853 は2年以上前に報告されました)、Raspberry Piやモバイルデバイスなどの小型デバイスへのバイナリの展開におけるGoの関心の高まりを踏まえ、Go 1.7リリースで注目を集めました。
Go 1.7で行われた作業
Go 1.7には、バイナリサイズに影響を与える3つの重要な変更があります。
1つ目は、このリリースでAMD64に対して有効になった新しいSSAバックエンドです。SSAの主な動機はパフォーマンスの向上でしたが、生成されたコードの改善によりサイズも小さくなりました。SSAバックエンドにより、Goバイナリサイズは約5%縮小されます。ARMやMIPSなどのよりRISCライクなアーキテクチャについては、Go 1.8でこれらのバックエンドがSSAに変換されたときに、より大きな効果が期待されます。
2つ目の変更はメソッドのプルーニングです。1.6までは、一部のメソッドが呼び出されなくても、使用されたすべての型上のすべてのメソッドが保持されていました。これは、インターフェースを通して呼び出される可能性があるか、reflectパッケージを使用して動的に呼び出される可能性があるためです。現在は、インターフェースと一致しないエクスポートされていないメソッドはコンパイラによって破棄されます。同様に、リンカーは、対応するリフレクション機能がプログラムのどこにも使用されていない場合、リフレクションを通じてのみアクセス可能な他のエクスポートされたメソッドを破棄できます。この変更により、バイナリサイズは5~20%縮小されます。
3つ目の変更は、reflectパッケージで使用されるランタイム型情報のよりコンパクトな形式です。エンコーディング形式は、当初、ランタイムとreflectパッケージのデコーダーをできるだけシンプルにするために設計されました。このコードを少し読みづらくすることで、Goプログラムのランタイムパフォーマンスに影響を与えることなく、形式を圧縮できます。新しい形式により、Goバイナリサイズはさらに5~15%縮小されます。Android向けにビルドされたライブラリとiOS向けにビルドされたアーカイブは、新しい形式にはポインタが少なくなり、それぞれが位置独立コードで動的な再配置を必要とするため、さらに縮小されます。
さらに、インターフェースデータレイアウトの改善、静的データレイアウトの改善、依存関係の簡素化など、多くの小さな改善が行われました。たとえば、HTTPクライアントは、もはやHTTPサーバー全体をリンクしません。変更の完全なリストは、issue #6853にあります。
結果
小さなツールから大規模な本番プログラムまで、さまざまなプログラムは、Go 1.7でビルドした場合、約30%小さくなります。
標準的なHello Worldプログラムは、2.3MBから1.6MBになります。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
デバッグ情報なしでコンパイルした場合、静的にリンクされたバイナリは1MB未満になります。

このサイクルのテストに使用された大規模な本番プログラムであるjujud
は、94MBから67MBになりました。
位置独立バイナリは50%小さくなります。
位置独立実行可能ファイル(PIE)では、読み取り専用のデータセクション内のポインタは動的な再配置を必要とします。新しい型情報形式はポインタをセクションオフセットに置き換えるため、ポインタごとに28バイト節約できます。
デバッグ情報が削除された位置独立実行可能ファイルは、モバイル開発者にとって特に重要です。これは、電話に出荷されるプログラムの種類だからです。大きなダウンロードはユーザーエクスペリエンスを低下させるため、この削減は朗報です。
今後の作業
ランタイム型情報に対するいくつかの変更は、Go 1.7のフリーズには間に合いませんでしたが、1.8に含まれることで、特に位置独立バイナリがさらに縮小されることを期待しています。
これらの変更はすべて保守的なものであり、ビルド時間、起動時間、全体の実行時間、またはメモリ使用量を増やすことなく、バイナリサイズを削減します。バイナリサイズをさらに削減するために、より抜本的な措置を講じることができます。実行可能ファイルを圧縮するためのupxツールは、起動時間とメモリ使用量の増加を犠牲にして、バイナリサイズをさらに50%縮小します。非常に小さなシステム(キーホルダーに存在する可能性のある種類)の場合、リフレクションのないGoバージョンをビルドできますが、そのような制限された言語が十分に有用かどうかは不明です。ランタイムの一部のアルゴリズムでは、キロバイトが非常に重要な場合、低速だがよりコンパクトな実装を使用できます。これらすべては、今後の開発サイクルでより多くの研究を必要とします。
Go 1.7のバイナリサイズ縮小に貢献してくださった多くの貢献者の方々に感謝いたします!
次の記事:サブテストとサブベンチマークの使用
前の記事:Go 1.7 リリース
ブログインデックス