2016-10-09 4 views
0

32ビットシステムで-O0-O1という異なる結果を与えるFortranプログラムがあります。プログラムが正確に同じ番号を記入する必要があり変数を格納すると、-O1で簡単な操作の結果が変わります

4.1838698196228139E-013 
20.148674000000000  
-0.15444754236171612 

:このdataファイルで

program test 
implicit none 
character foo 
real*8 :: Fact,Final,Zeta,rKappa,Rnxyz,Zeta2 

read(5,*) rKappa 
read(5,*) Zeta 
backspace(5) 
read(5,*) Zeta2 
read(5,*) Rnxyz 

Fact=rKappa/Sqrt(Zeta**3) 

write(6,'(ES50.40)') Fact*Rnxyz 

Fact=rKappa/Sqrt(Zeta2**3) 
Final = Fact*Rnxyz 
write(6,'(ES50.40)') Final 

end program test 

:違いを追跡、私は次のテストケース(test.f90)を思い付きました。 Zeta2Zetaと同じであることに注意してください。同じ番号が再度読み取られるからです(これはコンパイラが同じ番号であり、問​​題を隠すのを防ぐためです)。唯一の違いは、最初に操作が書き込み時に「オンザフライ」で行われ、結果が変数に保存され、変数が出力されることです。 -O1とそうでない、番号が同一である-O0とそう

$ gfortran -O0 -m32 test.f90 && ./a.out < data 
    -7.1447898573566615177997578153994664188136E-16 
    -7.1447898573566615177997578153994664188136E-16 

$ gfortran -O1 -m32 test.f90 && ./a.out < data 
    -7.1447898573566615177997578153994664188136E-16 
    -7.1447898573566605317236262891347096541529E-16 

を、:

は、今私はのgfortran 4.8.4(Ubuntuの14.04バージョン)でコンパイルして実行します。

Iは-fdump-tree-optimizedと最適化されたコードを確認しようとした:

final.10_53 = fact_44 * rnxyz.9_52; 
    D.1835 = final.10_53; 
    _gfortran_transfer_real_write (&dt_parm.5, &D.1835, 8); 
    [...] 
    final.10_63 = rnxyz.9_52 * fact_62; 
    final = final.10_63; 
    [...] 
    _gfortran_transfer_real_write (&dt_parm.6, &final, 8); 

私が見る唯一の違いは、一つのケースに印刷数がfact*rnxyzであることであり、他方ではrnxyz*factあります。これは結果を変えることができますか?ハイパフォーマンス・マークスの答えからは、いつどの変数がどのレジスタに行くのかということが関係していると思います。私も-Sで生成されたアセンブリの出力を見てみましたが、私はそれを理解するとは言いません。

そして、(64ビットマシン上)-m32フラグなしで、番号も同じです...

編集:私は-ffloat-storeまたは-mfpmath=sse -sse2を追加した場合の数字が同じである(最後に、hereを参照してください) 。コンパイラがデフォルトで387の計算を使用するので、私がi686マシンでコンパイルするとき、これは理にかなっています。

-mfpmath = SSE [...]

i386のコンパイラの場合:私は-m32て、x86-64のマシンでコンパイルする場合でも、それはマニュアルに従って必要とするべきではありませんSSE拡張を有効にしてこのオプションを有効にするには、-march=cpu-type,-msseまたは-msse2スイッチを使用する必要があります。 x86-64コンパイラの場合、これらの拡張機能はデフォルトで有効になっています。

[...]

これは、x86-64のコンパイラのデフォルトの選択肢です。

多分-m32はこれらの「デフォルト」を無効にしますか?ただし、gfortran -Q --help=targetを実行するとmfpmathは387となり、msse2は無効になります。

+0

アセンブリ生成コードを確認します。 O1は投稿したコードにいくつかの最適化を適用することがあります。さらにhttp://stackoverflow.com/questions/19618679/floating-point-optimizations-guidelineに興味があるかもしれません。 – Harald

+0

@Harald '-S'で出力をチェックしましたが、あまりにも曖昧になり始めました。いずれにしても、私はそこに違いは見られません。私は(AFAICT)関連の部分で質問を更新しています。 – Jellby

+0

@ハラルド私はテストで間違いを犯したので、診断は少し異なります。それは要因の順序の変更があるようです。 – Jellby

答えて

1

回答は長すぎますが、回答よりも疑いがあります。OPは、唯一の違いは、書き込み時に最初の操作は 「オンザフライ」で行われ、その後、結果を変数に保存され、 変数が印刷されていることである

を書き込みます。

私はx86_64アーキテクチャの内部80ビットf-p算術について考えています。 f-p算術演算のシーケンスの正確な結果は、中間値が80ビットから64ビットにトリムされるときに影響されます。そしてそれはあるコンパイラの最適化レベルとは異なるかもしれないものです。

O1バージョンのコードで表示される2つの数字の違いは、15桁目の10進数で、64ビットf-p算術で使用できる精度の限界についてです。

は、いくつかのより多くの周りいじる

-7.1447898573566605317236262891347096541529E-16 
のIEEE-754表現として

-7.1447898573566615177997578153994664188136E-16 

1 01111001100 1001101111011110011111001110101101101100011000001101 

のIEEE-754表現として

1 01111001100 1001101111011110011111001110101101101100011000001110 

を与えます

1の2つの数字は、その有意な部分が異なります。 O0のコンパイラは、f-p算術のためのIEEE-754規則に従う可能性があります(これらの規則は下位ビットでの丸めなどの点で厳密です)。O1は、より緩やかな算術の観点のみに従います。 (Fortran標準ではIEEE-754算術演算を使用する必要はありません)

最適化レベルを上げてIEEE-754規則を遵守するコンパイラオプションがあります。また、その遵守は測定可能なランタイムにかかります。

+0

残念なことに、実際のアプリケーションでは、このミスマッチは1e-08よりも大きくなります。たぶん、アルゴリズムとテストケースがあまりにも敏感であるかもしれません。あるいは、おそらく、これ以外の最終的な不一致の理由があります。 (注記の結論を修正しましたが、実際には要素の順序に違いがあります) – Jellby

+0

@Jellbyそして、その違いはいくつかの重要な意味を持っていますか、次のクリスマスの天気予報と2人の異なる回答者? –

+0

@VladimirFこれは、1e-08までの違いを受け入れるため、テストスイートを '-O0 'のある32ビットマシンで失敗させるほど重要です。しかし、私が上記のサンプルで得られたさまざまな結果を考えれば、代わりに '-O1'を使ってテストが失敗すると思いますので、問題は多分違っています。 – Jellby

関連する問題