gccのffast-mathは実際に何をしますか?


144

gccの--ffast-mathフラグを使用すると、浮動小数点演算の速度が大幅に向上し、IEEE標準の範囲外になることを理解していますが、オンになっているときに実際に何が起こっているかについての情報を見つけることができません。誰かが詳細のいくつかを説明して、フラグがオンまたはオフの場合に何かがどのように変化するかについての明確な例を示すことができますか?

同様の質問についてSOを掘り下げてみましたが、ffast-mathの仕組みを説明するものは何も見つかりませんでした。

回答:


86

おっしゃったように、厳密なIEEE準拠を維持しない最適化が可能です。

例はこれです:

x = x*x*x*x*x*x*x*x;

x *= x;
x *= x;
x *= x;

浮動小数点演算は結合的ではないため、演算の順序と因数分解は、丸めのために結果に影響します。したがって、この最適化は厳密なFP動作では行われません。

GCCがこの特定の最適化を実際に行うかどうかを実際に確認したことはありません。しかし、考え方は同じです。


25
@Andrey:この例では、3まで7回の乗算から行く
Mysticial

4
@Andrey:数学的には正しいでしょう。ただし、丸めが異なるため、最後の数ビットでは結果が少し異なる場合があります。
Mysticial、2011

1
ほとんどの場合、このわずかな違いは問題になりません(比較的は10 ^ -16程度ですdoubleが、アプリケーションによって異なります)。注意すべきことの1つは、ffast-math最適化は必ずしも「より多くの」丸めを追加するわけではないということです。IEEEに準拠していない唯一の理由は、書かれた内容と(少しではありますが)回答が異なるためです。
ミスティック

1
@user:エラーの大きさは入力データによって異なります。結果に比べて小さくなければなりません。たとえば、xが10より小さい場合、Mysticalの例のエラーは約10 ^ -10減少します。しかしの場合x = 10e20、エラーは数百万になる可能性があります。
Ben Voigt、2011

3
@stefanctそれ-fassociative-mathは実際に含まれている-funsafe-math-optimizationsものであり、次に含まれているのは-ffast-math なぜGCCが最適化さa*a*a*a*a*a(a*a*a)*(a*a*a)ないのですか?
phuclv 2018

255

-ffast-math は厳密なIEEEコンプライアンスを破るだけではありません。

まず第一に、もちろん、それは壊れます厳密なIEEEコンプライアンスに違反します。たとえば、浮動小数点で数学的には(理想的には)正確に同じではないものに命令を並べ替えることができます。

次に、単一命令の数学関数の後の設定を無効にしますerrno。これは、スレッドローカル変数への書き込みを回避することを意味します(一部のアーキテクチャでは、これらの関数に対して100%の違いが生じる可能性があります)。

第3に、すべての数学は有限であると想定しています。つまり、有害な影響を与える可能性のある場所でNaN(またはゼロ)のチェックが行われません。これが起こらないと単純に想定されています。

第4に、除算の逆近似と逆平方根を有効にします。

さらに、符号付きゼロ(ターゲットがサポートしている場合でも、符号付きゼロが存在しないとコードが想定)と丸め演算を無効にします。これにより、コンパイル時に定数の折りたたみが可能になります。

最後に、シグナリング/トラッピングの計算が原因でハードウェア割り込みが発生しないと想定するコードを生成します(つまり、ターゲットアーキテクチャでこれらを無効にできず、その結果発生しない場合、それらは処理されません)。


15
デイモン、ありがとう!いくつかの参照を追加できますか?同様gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html-ffast-math 設定-fno-数学のerrno、-funsafe-数学の最適化、-ffinite-数学のみ、-fno-丸め数学、-fno-シグナリング-nansと-fcx-limited-range。このオプションにより、プリプロセッサマクロFAST_MATHが定義されます。 "およびmath.hmath_errhandling付近などのglibcからの何か" デフォルトでは、すべての関数がerrnoと例外処理の両方をサポートします。gccの高速数学モードとインライン関数が定義されている場合、これは当てはまらない可能性があります。 "
osgx

4
@javapowered:「危険」かどうかは、必要な保証によって異なります。-ffast-mathコンパイラーがいくつかのコーナーをカットし、いくつかの約束を破ることを許可します(説明されているように)。これは一般にそれ自体は危険ではなく、ほとんどの人にとって問題ではありません。ほとんどの人にとって、それは同じですが、より速いだけです。ただし、コードこれらの約束を前提とし、それに依存している場合、コード予想とは異なる動作をする可能性があります。通常、これはプログラムがほとんど正常に動作しているように見えることを意味しますが、一部の結果は「予期しない」可能性があります(たとえば、物理シミュレーションでは、2つのオブジェクトが正しく衝突しない可能性があります)。
デイモン

2
@Royi:2つは互いに独立している必要があります。-O2サイズと速度をトレードオフするものを除いて、一般に「すべての」合法的な最適化を有効にします。-O3サイズと速度をトレードオフする最適化も有効にします。それでも100%の正確性を維持します。-ffast-math通常は害はないが、標準の文言では正しくないと見なされる「わずかに正しくない」動作を許可することにより、数学演算を高速化しようとします。2つのコンパイラでコードの速度が実際に大きく異なる場合(1〜2%だけではない)、コードが標準に厳密に準拠しており、...
Damon

1
...ゼロの警告を生成します。また、エイリアスルールや自動ベクトル化などの邪魔にならないようにしてください。原則として、GCCは少なくともMSVCと同じくらい(通常は私の経験ではより優れています)パフォーマンスを発揮する必要があります。そうでない場合、MSVCが無視するだけでGCCが最適化を無効にするような微妙な間違いをした可能性があります。両方が必要な場合は、両方のオプションを指定する必要があります。
デイモン

1
@Royi:そのコードは私には本当に小さくて単純なものではなく、数分(または数時間)で詳細に分析できるものではありません。とりわけ、それは一見無害なを含み#pragma omp parallel for、ループ本体内では、関数の引数が指すアドレスの読み取りと書き込みの両方を行っており、かなりの量の分岐を行っています。知識のない推測として、実装で定義されたスレッドの呼び出し内からキャッシュをスラッシングしている可能性があり、MSVCは、エイリアスルールが要求する中間ストアを誤って回避する場合があります。言うことは不可能です。
デイモン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.