2013-07-08

GCCの最適化オプション

郷に入っては郷に従えというが、GNU/Linuxのプログラミング環境にはまだまだ慣れない。とりあえず今のC++本執筆作業には、最新のClangでコンパイルできればそれで用が足りるので、Clangのビルドが楽なGNU/Linux環境は悪くない。欲を言えばlibc++もまともに動くようになってほしい。Ubuntu 12.10でlibsupc++を使ってlibc++をビルドしてみたところ、RTTI周りのABIに問題があるらしく、virtual関数呼び出しすらできなかった。

しかし、GNU/Linuxにおけるプログラミングはどうにも学ぶことが多すぎる。GNUのgdbの使い方もそろそろ覚えたいし、Autotoolsの使い方も覚えたいものだ。幸い、GNU/Linuxで一般的なテキストエディターであるVimは、特に何も学ばなくても使えるので楽だ。たかがテキスト編集作業に、多数のキーボードショートカットを覚えなければならないEmacsのようなOSは間違っていると思う。

とりあえず今のところ使い道はないが、GCCの最適化オプションを一晩学んでみた。

Optimize Options - Using the GNU Compiler Collection (GCC)

GCCには多数の最適化のためのオプションがあるが、最も一般的なものをまとめて有効にできるオプションがある。また、ドキュメントによれば、個々の最適化オプションを個別に有効になるよう指定しても、適切な-Oレベルが指定されていない場合は、単に無視されるのだという。

-O0

何も指定しない場合、これがデフォルトとなる。コンパイル速度を重視し、デバッグの際にも予期しないような結果にならない。

-O, -O1

これは最適化を有効にするオプションだ。これによって有効化される最適化はそれほど多くない。デバッグに支障のないものだけが有効化されるようだ。

-O2

GCCでデバッグを考慮せず最適化オプションを指定するというと、基本的にこれになる。ほとんどの最適化オプションはこれ一発で有効になる。

-O3

-O2では有効にされない最適化オプションをいくつか有効にする。効果の程は疑問だ。

とまあ、基本的にはこれだけだ。-O3でも有効にされない最適化オプションもあるが、それらは極端に限定的な効果だったり、また規格違反を犯す代わりに速くなるかもしれないといったようなオプションで、通常は使われない。後は例えばデフォルトで有効化されるが、無効化したい最適化オプションを個別に無効化するということもある。

その他にも興味深いオプションがある。

-Os

速度ではなくバイナリサイズが小さくなるように最適化する。-O2からバイナリサイズが大きくなる可能性のある最適化オプションを取り除いたもの。

-Og

これはGCC 4.8で追加された新しいオプションで、デバッグ機能を維持しつつ、速度も最適化するというオプションだ。デバッグ機能に支障をきたさない最適化オプションが有効にされる。編集してコンパイルしてデバッグといった作業にオススメだ。

-flto

LTO(Link Time Optimization)を有効にするオプション。これにより翻訳単位を超えた最適化が可能になる。

手元で怪しいマイクロベンチマークをしてみたり、またUbuntuのレポジトリにあるソフトウェアなどを最適化オプションを変えて自前でビルドしてみた結果、だいたい以下のような感想を持った。

-O2は明らかに効果が実感できる。-O3と-O2の違いは、コンパイル速度、実行速度ともに全く実感できない。LTOはコンパイル時間が極端に伸びるだけで、まったく効果が実感できない。一部のソフトウェアはLTOでビルドすると実行時に即座にセグフォる。

最後に、

-march, -mtune

これは特定のプラットフォームでのみサポートされているオプションで、特定のCPU向けに最適化する。とくに、いくつかプラットフォームで、-march=nativeとすると、コンパイル環境のコンピューターのCPUが判定され、そのCPU向けの最適化を行う。

と、特にこのようなプラットフォーム固有のコンパイル環境に特化した最適化オプションについて学ぶと、Gentooを入れたくなってしまう。しかしそれは、危険な誘惑だ。

このGentoo誘惑に負けそうな要因としては、最適化オプションを学ぶ際の余興として、普段使っているUbuntuのレポジトリにあるバイナリパッケージをいくつか、自前でCXXFLAGS='-O2 -march=native'などしてビルドしてみると、なんとパフォーマンスが実感できるほど向上したという事実がある。これはもだしがたい。

5 comments:

Anonymous said...

GCC4.7からは-Ofastオプションもありますね。-O3以上に最適化オプションを有効にするのですが、効果はそれほどでもないので大抵-O2で済むのが現状です。

江添亮 said...

-Ofastは規格違反の挙動を許すためあえて紹介しなかったのです。

Egtra said...

それ以外では、-O2 -fno-strict-aliasingを比較的よく見かける印象があります。それはつまりソースコードがバグっているのを隠蔽しているだけではないかと思うのですが。

江添亮 said...

エイリアス指定はC++11でようやく規格でサポートされましたからねぇ。

Anonymous said...

OpenMPを有効にする-fopenmpというオプションもありますね。

また、-fltoはいろいろ試したのですが、コンパイル時に-O2や-O3と併用してもほとんど効果ないです。
コンパイル時に-Og(未対応環境なら-Os)を指定し、リンク時に-O2、-O3を指定する方が効果的です。

環境変数だとCFLAGSとLDFLAGSで別個に-Oオプションをつけることで対応できます。