この記事の要点
- Swift 4.1 のコンパイラに、実行性能ではなく コードサイズの最小化 を狙う新しい最適化モード
-Osizeが追加されました。コマンドラインでは-Oの代わりに-Osizeを指定し、Xcode 9.3 では「Swift Compiler – Code Generation」のビルド設定から選べます。 - 一部のプロジェクトでは
-Osizeによってコードサイズが 5% から 30% 削減されました。性能への影響はプロジェクト次第ですが、多くのアプリでは低下は無視できる程度(5% 未満)です。ただし性能が重視されるコードでは引き続き-Oが適切な場合があります。 -Osizeは single-file compilation でも whole-module compilation でも動作し、whole-module の方がより良い最適化結果が得られます。Swift 4.1 では最適化モード(-O/-Osize)とコンパイルモード(single-file / whole-module)を独立して選べるようになりました。
背景: 最適化とコードサイズのトレードオフ
Swift コンパイラは強力な最適化を備えており、-O を指定すると最大限の実行性能を引き出すようにコードを変換します。しかし、この実行性能の向上は、しばしば コードサイズの増加 とのトレードオフになります。
-Osize モードは、この選択肢を利用者に与えるものです。最大速度ではなく最小のコードサイズを優先してコンパイルしたい場合に、-O の代わりに -Osize を使います。
コード最適化への影響
-Osize でも、コンパイラは -O と同じようにコードを最適化します。違いは、-Osize ではコンパイラが コードの重複を避けようとする 点です。
インライン化のサイズ上限を下げる
たとえば関数のインライン化(inlining)では、-Osize はある関数をインライン化すべきか判断するためのサイズ上限を低く設定します。
ただし、インライン化を完全に無効化するのは得策ではありません。小さな関数のインライン化は、むしろコードサイズの削減につながることが多いからです。たとえば次のような単純な getter を考えます。
struct X {
var x: Int { return 27 }
}
この getter を呼び出すための呼び出しオーバーヘッドは、関数をインライン化する場合より大きくなります。これは極端な例ですが、ある程度のサイズまではインライン化がコードサイズの改善に寄与します。
さらに、インライン化は別の最適化を誘発し、その結果コードサイズを減らせることもあります。次の例では、getter a.x をインライン化すると a.x が 27 に評価されると分かるため、if ブランチ全体を取り除けます。
func foo(a: X) {
if a.x != 27 {
// Can be optimized away if the getter of a.x is inlined
}
}
ヘルパー関数への抽出
インライン化以外にも、-Osize はコードサイズに特化した最適化を行います。たとえば、ジェネリック型の処理や Objective-C bridging のための一部のコードパターンを、インラインで生成せずにヘルパー関数として抽出します。これによって同じパターンの重複を避けます。
まとめ
-Osize モードは、極端に性能が重視されるわけではないプログラムのコードサイズを削減する有効な手段です。当時の記事は試用とフィードバックを呼びかけており、フォーラムでは osize タグで体験を共有するよう案内していました。
関連リンク
- Swift 3 における whole-module optimization —
-Osizeでもより良い結果が得られるとされる whole-module compilation の解説です。