最適化オプションの使い分け

プログラムを実装する際に必要になるコンパイルは、状況に応じて効率化や高速化をおこなっています。意識していないところで最適化され、リソースの節約になる反面、意図しない動作を引き起こす原因になることもあります。コンパイル時の最適化はオプションを使い分けることで、期待している状態と近いビルドができます。

最適化オプションの使い分け

コンパイルの最適化オプション(-O0、-O2、-Os)における、実行速度・バイナリサイズ・コンパイル時間の比較図

コンパイル時の最適化オプションとそれぞれの使い分け方を記載します。

-O0(最適化なし)

パフォーマンス改善の指標として、改善前の基準(ベースライン)を得るために、最適化をおこなっていない状態のコードが欲しいときにも使用します。ただし、生成されるコードは最適化されないぶん実行速度が遅く、サイズも大きくなるため、デバッグ時以外での使用は推奨されません。

-O2(性能を最適化する)

コンパイル後のサイズやコンパイル時間に関係なく、パフォーマンス向上を計るオプションです。-O0と比べるとコンパイルに時間がかかる傾向がありますが、生成したコードの効率が良くなります。最適化はほぼ全て適用されるため、コードの実行に特別な理由がない限りは、この最適化オプションが推奨されます。一部パッケージを壊すようなこともないため、全体にまとめて適用が可能です。

-Os(サイズを最適化する)

-O2の最適化を一度全て有効にした上で、最適化によって生成されるバイナリの容量が増える最適化を中止するオプションです。バイナリのサイズを意識したオプションのため、実行先のキャッシュが小さかったり、ディスクの空き容量が少なかったりするときに有効なオプションです。

より積極的にサイズを小さくする-Ozもありますが、実行時のパフォーマンスが劣化するため、バランスよく容量を抑えるなら-Osの方が有効であることが多いです。

デバッグ不能にならないための運用

実行時に不具合が出たときに原因がわからないと、直しようがありません。デバッグ不能に陥らないための運用のポイントを示します。

明確な使い分け

コンパイルオプションはそれぞれ明確に目的が異なります。先に紹介した-O0は正常にコンパイル、実行、デバッグができるかの確認を目的としており、そのままリリースするとリソースの無駄遣いなどが発生しかねませんリリース後の実運用を見据えるなら-O2や-Osで最適化を図る必要があります。-O2はパフォーマンスとコンパイル時間のバランスを取ってくれるコンパイル方法ですが、オプションの値を使うことで、より細かい調整が可能です。

同時に使わない

デバッグオプションが必要とする情報を、最適化オプションが消したり、安易に効率化してしまったりすることにより、デバッグが機能しなくなる可能性が高くなります。最適化によって行や変数が削除されると、デバッグ時に止めたいところでブレークポイントを設定できず、プログラムの状態を見分けることができなくなります。C言語の場合はスコープが変わることによって、変数が参照できなくなる可能性もあります。

もちろん最適化を実施しなくても同様の事象を引き起こす可能性はありますが、最適化オプションを適用した後では発生する確率が高まります。

部分的に使用する

最適化されてしまうことにより、デバッグで検出したいエラーの原因となるコードが特定できない場合があります。GCCでは関数単位で適用したい最適化のオプションを変更することが可能です。エラーの対象となる調査対象箇所がある程度絞られている場合は、そのメソッドだけ最適化せずにデバッグを動かすと原因特定を早められます。

オプションを使わないとコンパイル自体に時間がかかることもあるため、開発が進んでくると-O0でのデバッグは時間がかかりすぎます。デバッグしたいところだけコードで明確にコンパイルオプションを指定し、それ以外は最適化をすると効率よくデバッグを進められます。

volatileの適切な使用

コンパイル時の最適化を抑制するのがvolatileです。データの受信を待ってから処理をしたい、CPUの外部から書き換えられるメモリやレジスタを参照したいなど、最適化されると削除や変更されてしまうおそれのある処理をそのままコンパイルすることができます。最適化されて動作エラーを起こすリスクを排除するためにも、初めからvolatileつきで宣言しておくと安心です。

volatileを使ったところだけ最適化が無視されるため、意図どおりの処理がおこなえます。参照する変数が最適化されてしまう場合もvolatileを使えば、最適化をしなかったメソッドの外から変数を呼び出せます。

組み込みソフトの世界 トップへ戻る