GCC optimization/ja

このガイドは、コンパイル済みコードを安全で分別のある CFLAGS と CXXFLAGS を使って最適化する手法を紹介します. また、一般的な最適化の背景にある理論について述べます.

CFLAGSとCXXFLAGSとは？
CFLAGS と CXXFLAGS は、ソースコードをコンパイルするときに使われるオプションをGNUコンパイラコレクションであるGCCに伝えるための環境変数です. CFLAGS はCで書かれたソースコード、 CXXFLAGS はC++で書かれたソースコード用になります.

これらはプログラムのデバッグメッセージの量を減らすために使われたり、エラーや警告のレベルを増加させたり、また、もちろん生成されるコードの最適化にも使われます. GCC manualに利用可能なフラグとその効果の完全なリストが記載されています.

どのように使われているのでしょうか？
CFLAGS and CXXFLAGS can be used in two ways. First, they can be used per-program with Makefiles generated by the program.

However, this should not be done when installing packages found in the Portage tree. Instead, for Gentoo-based machines, set the CFLAGS and CXXFLAGS variables in This way all packages will be compiled using the options specified in

As seen in the example above the CXXFLAGS variable is set to use all the options present in CFLAGS. Most every system should be configured in this manner; additional options for CXXFLAGS are extremely rare in common use cases.

よくある誤解
While CFLAGS and CXXFLAGS can be very effective means of getting source code to produce smaller and/or faster binaries, they can also impair the function of the code, bloat its size, slow down its execution time. Setting them incorrectly can even cause compilation failures!

CFLAGS are not a magic bullet; they will not automatically make the system run faster or reduce the size of binaries on the disk. Adding too many flags in an attempt to optimize (or "rice") the system is a sure recipe for failure. The point of diminishing returns is reached rather quickly when dealing with CFLAGS.

Despite the boasts and brags found on the internet, aggressive CFLAGS and CXXFLAGS are far more likely to harm binaries than to do any good. Keep in mind the flags are designed to be used at specific places for specific purposes. Few flags work as intended globally.

準備はできましたか？
Being aware of the risks involved, take a look at some sane, safe optimizations. These will hold in good stead and will be endearing to developers the next time a problem is reported on Bugzilla. (Developers will usually request the user to recompile a package with minimal CFLAGS to see if the problem persists. Remember: aggressive flags can ruin code!)

基本
The goal behind CFLAGS and CXXFLAGS is to create code tailor-made to your system; it should function perfectly while being lean and fast, if possible. Sometimes these conditions are mutually exclusive, so this guide will stick to combinations known to work well. Ideally, they are the best available for any CPU architecture. For informational purposes, aggressive flag use will be covered later. Not every option listed on the GCC manual (there are hundreds) will be discussed, but basic, most common flags will be reviewed.

-march
最初の、そしてもっとも重要なオプションは です. このオプションはコンパイラに対してどのシステムプロセッサアーキテクチャ (もしくは"arch")のためのコードを生成するのかを指示します. つまり、特定のCPU向けのコードを生成すべきであるといっているのです. CPUが違えば、性能が異なり、異なる命令セットをサポートし、コードの実行方法も違います. フラグは、あなたのCPUの全ての性能、特徴、命令セット、癖などに合わせて特化したコードを生成するようにコンパイラに伝えます.

たとえに書いてある 変数を一般的なアーキテクチャに設定していても、 を設定すれば、プログラムは指定したプロセッサ向けに最適化されるでしょう. x86とx86-64のCPUは(とりわけ) フラグを使うべきです.

どんな種類のCPUを使っていますか？以下のコマンドを実行すれば、それが分かります.

To get more details, including  and   values, two commands can be used.


 * The first command tells the compiler not to do any linking, and instead of interpreting the  option for clarifying command line options, it now shows if certain options are enabled or disabled . In this case, the options shown are those enabled for the selected target:


 * The second command will show the compiler directives for building the header file, but without actually performing the steps and instead showing them on the screen . The final output line is the command that holds all the optimization options and architecture selection:

では実際に を見てみましょう. この例は古いPentium IIIチップ向けです.

こちらはAMD64向けです.

CPUのタイプが決められない場合、またはどの設定を使えばいいかわからない場合、 を使うことができます. このフラグを使った場合、GCCはプロセッサを判別して、自動的にふさわしいフラグを設定するでしょう. しかしながら、このフラグは異なるCPU向けにパッケージをコンパイルする目的では使用すべきではありません！

例えば、あるコンピュータでパッケージをコンパイルして、しかしそれらを別のコンピュータで実行しようとしている場合 (処理の早いコンピュータで、古くて遅いマシンのためにビルドしているときなど)、 を使わないでください. "native"というのはコンパイルしているマシンのCPUタイプのみに特化して、アプリケーションのコードを生成することを意味しています. AMD Athlon 64上で と共にビルドされたアプリケーションは、古いVIA C3では実行することができないでしょう.

また、 と フラグも利用可能です. これらのフラグはたいてい フラグが利用できない場合にのみ使われます. 例えば特定のプロセッサアーキテクチャは や が必要になるかもしれません. 残念ながら、GCCの挙動はそれぞれのフラグの振る舞いが、あるアーキテクチャから近いアーキテクチャであってもあまり一貫性はありません.

x86とx86-64のCPUにおいて、 は利用可能な命令セットと正しいABIを使い、そのCPUに特化したコードを生成するでしょう. そのため古かったり種類の異なるCPUとの後方互換性は持っていません. i386やi486のような古いCPU向けにコードを生成する必要があるときのみ、 の使用を考慮するべきでしょう. は よりも一般的なコードを生成します. 特定のCPUコードにチューニングしますが、利用可能な命令セットやABIを考慮しないのです. はx86やx86-64のシステム上では非推奨となっているので、使わないでください.

x86/x86-64でない(SparcやAlpha、PowerPCのような)CPUでのみ、 の代わりに や が必要になるでしょう. これらのアーキテクチャ上では、 / は(x86/x86-64上での) と同じように振る舞うでしょう･･･しかしフラグの名前は違うのです. 繰り返しますが、GCCの振る舞いとフラグ名はアーキテクチャを超えて一貫していないので、システムでどのフラグを使うべきなのかをGCCのマニュアルで必ず確認してください.

-O
次は フラグについてです. これは全体の最適化レベルをコントロールしますが、特にこの最適化レベルを上げることによって、ソースコードのコンパイルの時間がいくらか増えたり、よりたくさんのメモリを使用するようになります.

７つの 設定があります. 、 、 、 、 、 、 です. で指定できるのはこのうちの一つだけです.

を除いて、 の設定はいずれもいくつかの追加フラグを有効にします. なので、どの レベルで、どのフラグが有効になり、そのフラグにどんな効果があるのかを学ぶために、GCCマニュアルのoptimization options の章を読んで確認しましょう.

では、それぞれの最適化レベルを見てみましょう.


 * : This level (that is the letter "O" followed by a zero) turns off optimization entirely and is the default if no  level is specified in CFLAGS or CXXFLAGS . This reduces compilation time and can improve debugging info, but some applications will not work properly without optimization enabled. This option is not recommended except for debugging purposes.


 * : これは最も基本的な最適化レベルです. コンパイラは時間をたくさんかけることなく、高速でサイズの小さなバイナリを生成しようと試みるでしょう. これは基本的な最適化しかおこないませんが、その代わり、いつでもうまくいくはずです.


 * : から更に踏み込みます. これは特別な理由がない限り"推奨される"最適化レベルです.  は で有効になるものに加え、さらにいくつかのフラグを有効にします.  を使うと、コンパイラはサイズとコンパイル時間に妥協せずにコードのパフォーマンスを改善しようと試みます.


 * : これは取りうる最も高い最適化レベルです. コンパイル時間とメモリ使用量を犠牲にして最適化を実施します. ただし は性能を改善する保証がありません. 実際多くのケースで、バイナリサイズが大きくなり、メモリ使用量が増えることで逆にシステムが遅くなります. はいくつかのパッケージを壊すことがわかっています. これらの理由により は推奨されません.


 * : このレベルはバイナリのサイズを重視して最適化するでしょう. これは フラグの中で、生成されるバイナリのサイズが増えないものを全て有効にします. CPUのキャッシュが小さかったり、ディスクの空き容量が極端に限られている場合などに非常に有効でしょう.


 * : GCC 4.8では新しい汎用的な最適化レベル が導入されました. このレベルはコンパイル時間の短縮とデバッグエクスペリエンスを向上させる一方、妥当なランタイム性能を確保することを目的としています. 結果的に開発全体で得られるものはデフォルトの最適化レベル より向上するはずです. と は同じでないことだけ理解しておいてください.  はデバッガとやりとりするために単純に最適化をオフにするだけです.


 * : GCC 4.7では、 に加えて 、 、 が利用できます. このオプションは規格への厳密な適合を犠牲にするため推奨されません.

As previously mentioned,  is the recommended optimization level. If package compilation fails and while not using, try rebuilding with that option. As a fallback option, try setting the CFLAGS and CXXFLAGS to a lower optimization level, such as  or even   (for error reporting and checking for possible problems).

-pipe
よく使うフラグに があります. このフラグは、生成されるバイナリ自体には何の影響もありませんが、コンパイル時間が短縮されます. これはコンパイルにおける各処理の間で一時ファイルを使う代わりにパイプを使うように指示します. これにより多くのメモリを使うことになります. メモリに余裕のないシステムの場合、GCCが強制終了するかもしれません. そのような場合はこのフラグを使わないでください.

-fomit-frame-pointer
This is a very common flag designed to reduce generated code size. It is turned on at all levels of  (except  ) on architectures where doing so does not interfere with debugging (such as x86-64), but it may need to be activated. In that case add it to the flags. Though the GCC manual does not specify all architectures, it is turned on by using the  option. It's still necessary to explicitly enable the  option, to activate it on x86-32 with GCC up to version 4.6, or when using   on x86-32 with any version of GCC. However, using  will make debugging hard or impossible.

In particular, it makes troubleshooting applications written in Java much harder, though Java is not the only code affected by using this flag. So while the flag can help, it also makes debugging harder; backtraces in particular will be useless. When not doing software debugging and no other debugging-related CFLAGS such as  have been used, then try using.

-msse、-msse2、-msse3、-mmmx、-m3dnow
これらのフラグはStreaming SIMD Extentions (SSE)、SSE2、SSE3、MMX、[https://ja.wikipedia.org/wiki/3DNow! 3DNow!]の命令セットをx86とx86-64アーキテクチャで有効にします. これらは主にマルチメディアやゲーム、その他の浮動小数点を多用する計算処理に向いています. その他にも有用な数学用機能の向上をいくつか含んでいます. 比較的新しいCPUならば、これらの命令セットに対応しています.

通常、正しい を使っている限り、これらのどのフラグもに加える必要はありません(例えば は を有効にします). いくつかの注意すべき例外は、比較的新しいVIAとAMD64のCPUです. VIAとAMD64はこれらの命令をサポートしますが、(SSE3のように) では有効になりません. これらのCPUについてはの出力を確認して、ふさわしいフラグを追加する必要があるでしょう.

-funroll-loopsや-fomg-optimizeを使ったら速くなったんだけど！
いいえ違います. フラグを付け加えれば付け加えるほど最適化されると言う誰かに騙されているだけです. システム全体で挑戦的なフラグを使うことはあなたのアプリケーションを傷つけるでしょう. GCC マニュアルでは と を使うとバイナリは大きくなり、実行も遅くなると述べています. またいくつかの理由から、これらの二つのフラグと同時に、 や や などの似たようなフラグが、速度を最大限誇示したい人たちの間で、とても人気を博しています.

ここで本当に問題なのは、これらのフラグは危険なほどに挑戦的なフラグということです. それらのフラグが何をやらかしているのかGentoo ForumsとBugzillaあたりをよく見てください. ろくなことないですよ！

You do not need to use those flags globally in CFLAGS or CXXFLAGS. They will only hurt performance. They may make you sound like you have a high-performance system running on the bleeding edge, but they don't do anything but bloat the code and get your bugs marked INVALID or WONTFIX.

あなたはそのような危険なフラグを使う必要はありません. いえ、'''使わないでください. ''' 、 、 という基本を守り通してください.

3より高い-Oレベルはどう？
Some users boast about even better performance obtained by using,  , and so on, but the reality is that   levels higher than 3 have no effect. The compiler may accept CFLAGS like, but it actually doesn't do anything with them. It only performs the optimizations for, nothing more.

さらに証拠が必要ですか？ソースコードを試してみてください.

見てのとおり、3より高いレベルであっても、結局 として扱われます.

実際のマシンと別のマシンでコンパイルするのはどう？
何人かの読者は、あきらかに劣ったCPUやGCCサブアーキテクチャでコンパイルすることを避けるために他のマシンでコンパイルすることは、（ネイティブな環境でのコンパイルと比較して）劣った最適化になるのか知りたくなるでしょう. 答えは単純でいいえです. コンパイルが走る実際のマシンに関係なく、またGCCをビルドしたときのCHOSTに関係なく、同じ引数が使用されている限り ( は除く) 、そしてGCCのバージョンが同じである限り (マイナーバージョンが違うかもしれません) 、最適化の結果は厳密に同じです.

例を一つあげます. GCCのCHOSTがi686-pc-linux-gnuとなっているマシンにGentooをインストールして、CHOSTがi486-linux-gnuとなっている別のPCにDistccサーバーをインストールします. リモートサーバーのコンパイラのサブアーキテクチャもしくはハードウェアが明らかに劣っている場合、最適化が十分なされないのではと心配する必要はありません. 結果は、両方のコンパイラに同じ引数が与えられている限り（かつ に が与えられていない限り）、ネイティブにビルドしたときと同じ最適化がかかります. ただしこの特殊なケースではDistcc and -march=nativeで説明されている通り、ターゲットのアーキテクチャが明示的に指定されなければなりません.

異なるサブアーキテクチャに向けてビルドされた２つのGCCの動作には一つしか違いがありません. それは暗に与えられるデフォルトの です. コマンドラインで を明示的に指定しなかった場合、GCCのCHOSTに設定された値が使用されます.

冗長なフラグ指定はどう？
Oftentimes CFLAGS and CXXFLAGS that are turned on at various  levels are specified redundantly in. Sometimes this is done out of ignorance, but it is also done to avoid flag filtering or flag replacing.

Flag filtering/replacing is done in many of the ebuilds in the Portage tree. It is usually done because packages fail to compile at certain  levels, or when the source code is too sensitive for any additional flags to be used. The ebuild will either filter out some or all CFLAGS and CXXFLAGS, or it may replace  with a different level.

Gentoo Developer Manualにフラグの除去と置換がどのような場合に、どのように使われているのか概要が記載されています.

特定のレベルに対して重複してフラグを設定することによって、 に対するフラグ除去をある程度回避することができます. 例えば、 であれば次のようにします.

However, this is not a smart thing to do. CFLAGS are filtered for a reason! When flags are filtered, it means that it is unsafe to build a package with those flags. Clearly, it is not safe to compile your whole system with  if some of the flags turned on by that level will cause problems with certain packages. Therefore, you shouldn't try to "outsmart" the developers who maintain those packages. Trust the developers. Flag filtering and replacing is done for your benefit! If an ebuild specifies alternative flags, then don't try to get around it.

開発者が許可していないフラグでパッケージをビルドすれば、大概は数々の問題に陥り続けることでしょう. Bugzillaに不具合を報告する際には、で使っているフラグは容易く見抜かれてしまうので、余計なフラグを除いて再コンパイルする様に開発者に指示されるでしょう. 初めから冗長なフラグを指定しないことで、再コンパイルする手間を省いてください！ あなたが開発者よりよく知っていると根拠なく無意識に決めつけないでください.

LDFLAGSはどう？
The Gentoo developers have already set basic, safe LDFLAGS in the base profiles, so they do not need to be changed.

パッケージごとにフラグを変更出来るの？
Information on how to use per-package environment variables (including CFLAGS ) is described in the Gentoo Handbook, "Per-Package Environment Variables".

資料
以下、最適化について理解を深めるための資料を紹介します.


 * GCC online documentation


 * Gentoo Handbook - Configuring compile options


 * man make.conf


 * ウィキペディア


 * Gentooフォーラム