268

-O3さまざまな情報源から (主に私の同僚からですが)、 g++ の最適化レベルでコンパイルすることは何らかの形で「危険」であり、必要であることが証明されない限り、一般的に避けるべきであると聞いています。

これは本当ですか? もしそうなら、なぜですか? 私はただ固執する必要があり-O2ますか?

4

5 に答える 5

260

gcc (2.8 など) の初期の頃と egcs の時代、redhat 2.96 -O3 は時々かなりバグがありました。しかし、これは 10 年以上前のことであり、-O3 は他のレベルの最適化と大差ありません (バグの点で)。

ただし、言語のルール、特にコーナーケースにより厳密に依存しているため、人々が未定義の動作に依存しているケースが明らかになる傾向があります。

個人的なメモとして、私は長年にわたって -O3 を使用して金融部門で実動ソフトウェアを実行しており、-O2 を使用していれば発生しなかったであろうバグにまだ遭遇していません。

一般的な要望により、ここに追加:

-O3 と、特に -funroll-loops (-O3 によって有効にされない) のような追加のフラグにより​​、より多くのマシン コードが生成されることがあります。特定の状況下 (例: 非常に小さな L1 命令キャッシュを備えた CPU) では、一部の内部ループのすべてのコードが L1I に適合しなくなったため、速度が低下する可能性があります。通常、gcc はそれほど多くのコードを生成しないように非常に懸命に努力しますが、通常は一般的なケースを最適化するため、これが発生する可能性があります。特にこれが発生しやすいオプション (ループ展開など) は、通常 -O3 には含まれず、マンページで適切にマークされています。そのため、高速なコードを生成するには -O3 を使用し、適切な場合 (たとえば、プロファイラーが L1I ミスを示した場合) にのみ -O2 または -Os (コード サイズの最適化を試みる) にフォールバックすることをお勧めします。

最適化を極限まで進めたい場合は、特定の最適化に関連するコストを --param を介して gcc で微調整できます。さらに、gcc には、これらの関数だけの最適化設定を制御する関数に属性を配置する機能があることに注意してください。そのため、1 つの関数で -O3 に問題がある場合 (またはその関数だけに特別なフラグを試してみたい場合)、ファイル全体やプロジェクト全体を O2 でコンパイルする必要はありません。

-Ofast を使用する場合は、次のように注意する必要があるようです。

-Ofast は、すべての -O3 最適化を有効にします。また、すべての標準準拠プログラムに有効ではない最適化も有効にします。

これは、-O3 が完全に標準に準拠することを意図していると結論付けます。

于 2012-07-18T16:40:09.277 に答える
50

私の多少チェックされた経験では-O3、プログラム全体に適用すると、プログラムが命令キャッシュに収まらなくなる積極的なループの展開とインライン化がオンになるため、ほとんどの場合、( に比べて) 遅くなります。大規模な-O2プログラムの場合、これは!-O2-Os

の意図された使用パターン-O3は、プログラムのプロファイリングを行った後、これらの積極的なスペースと速度のトレードオフから実際に恩恵を受ける重要な内部ループを含む少数のファイルに手動で適用することです。-O3GCC の新しいバージョンには、(IIUC)ホット関数に最適化を選択的に適用できるプロファイル ガイド付き最適化モードがあり、このプロセスを効果的に自動化します。

于 2013-11-14T18:46:17.693 に答える
17

-O3 オプションは、下位レベルの '-O2' および '-O1' のすべての最適化に加えて、関数のインライン化などのよりコストのかかる最適化をオンにします。'-O3' 最適化レベルを使用すると、結果の実行可能ファイルの速度が向上する可能性がありますが、サイズも大きくなる可能性があります。これらの最適化が好ましくない状況下では、このオプションにより実際にプログラムが遅くなる可能性があります。

于 2012-07-18T18:12:48.350 に答える
16

はい、O3 の方がバグが多いです。私はコンパイラ開発者であり、独自のソフトウェアを構築する際に O3 がバグのある SIMD アセンブリ命令を生成することによって引き起こされる明確で明白な gcc バグを特定しました。私が見た限りでは、ほとんどのプロダクション ソフトウェアは O2 と共に出荷されます。つまり、O3 はテストやバグ修正に関してあまり注目されません。

O3 は O2 の上にさらに変換を追加し、O1 の上にさらに変換を追加します。統計的に言えば、より多くの変換はより多くのバグを意味します。これは、どのコンパイラにも当てはまります。

于 2016-08-25T20:17:14.183 に答える
3

最近、最適化を使用して問題が発生しましたg++。この問題は、(コマンドおよびデータ用の) レジスタがメモリ アドレスで表される PCI カードに関連していました。私のドライバーは、物理アドレスをアプリケーション内のポインターにマップし、呼び出されたプロセスに渡しました。これは、次のように機能します。

unsigned int * pciMemory;
askDriverForMapping( & pciMemory );
...
pciMemory[ 0 ] = someCommandIdx;
pciMemory[ 0 ] = someCommandLength;
for ( int i = 0; i < sizeof( someCommand ); i++ )
    pciMemory[ 0 ] = someCommand[ i ];

カードは期待どおりに動作しませんでした。アセンブリを見たとき、コンパイラは にのみ書き込み、先行するすべての書き込みを省略していることを理解しましsomeCommand[ the last ]pciMemory

結論として、最適化には正確かつ注意を払う必要があります。

于 2013-03-25T15:01:43.287 に答える