確定的モデルの実行での小さく予測できない結果


10

私はかなり大きなモデル(約5000行)をCで記述しています。これはシリアルプログラムであり、乱数の生成はどこにもありません。FFTを使用する関数にFFTWライブラリを使用します。FFTW実装の詳細はわかりませんが、その中の関数も確定的であると想定しています(エラーが発生した場合は修正してください)。

私が理解できない問題は、同じマシン(同じコンパイラ、同じライブラリ)での同一の実行の結果に小さな違いがあることです。

私は、倍精度変数を使用して、変数に結果を出力するvalue例えば、私が発行します fprintf(outFID, "%.15e\n", value);
fwrite(&value, 1, sizeof(double), outFID);

そして、私は常に次のような違いを得るでしょう:
2.07843469652206 4 e-16対2.07843469652206 3 e-16

私はこれがなぜなのかを理解するために多くの時間を費やしてきました。私は最初、メモリチップの1つが故障していると思っていたので、注文して交換しましたが、役に立ちませんでした。その後、同僚のLinuxマシンでコードを実行してみたところ、同じ性質の違いが生じました。

何が原因でしょうか?今は小さな問題ですが、「氷山の一角」(深刻な問題)なのでしょうか。

数値モデルを扱う誰かがこの問題に遭遇した場合に備えて、StackOverflowの代わりにここに投稿すると思いました。誰かがこれに光を当てることができれば、私は多くの義務があります。

コメントの
フォローアップ Christian ClasonとVikram:まず、私の質問に関心をお寄せいただきありがとうございます。あなたがリンクした記事は、次のことを示唆しています:1.丸め誤差は精度を制限し、2。異なるコード(一見害のない印刷ステートメントを導入するなど)がマシンのイプシロンまでの結果に影響を与える可能性があります。効果fwritefprintf機能を比較していないことを明確にすべきです。どちらか一方を使用しています。特に、両方の実行で同じ実行可能ファイルが使用されます。fprintfOR を使用しているかどうかに関係なく、問題が発生すると単に述べていfwriteます。

したがって、コードパス(および実行可能ファイル)は同じであり、ハードウェアも同じです。これらすべての外部要因が一定に保たれている場合、基本的にランダム性はどこから来るのでしょうか?不良メモリがビットを正しく保持していないためにビットフリップが発生したのではないかと疑ったので、メモリチップを交換しましたが、これは問題ではないようです。私のプログラムは、1回の実行で数千のこれらの倍精度数値を出力します。ランダムなビットフリップを持つランダムな握りが常にあります。

クリスチャンClasonの最初のコメントへのフォロー:なぜマシンの精度内で0と同じ?doubleの最小の正の数は2.22e-308なので、0に等しくないはずです。私のプログラムは10 ^ -16の範囲(1e-15から8e-17の範囲)で数千の値を出力し、私たちの研究プロジェクトには意味のある変化が見られたので、無意味なものを見ていなかったと思います番号。21016

フォローアップ#2
これは、コメントでの派生的議論を支援するために、モデルによって出力された時系列のプロットです。 ここに画像の説明を入力してください


21016

なぜあなたの機械は機械の精度よりも正確でないのかと尋ねています。en.wikipedia.org/wiki/Machine_epsilon
Vikram

1
浮動小数点演算に対するコードパスの微妙な影響の関連例については、inf.ethz.ch / personal / gander / Heisenberg / paper.htmlを参照してください。そしてもちろん、ece.uwaterloo.ca /〜dwharder / NumericalAnalysis / 02Numerics / Double /…
Christian Clason

1
1016

2
1

回答:


9

これらの種類の違いを引き起こす可能性がある本質的に非決定的である現代のコンピューティングシステムの側面があります。ソリューションの必要な精度と比較して差が非常に小さい限り、これについて心配する理由はおそらくありません。

私自身の経験に基づいて問題が発生する可能性のある例。2つのベクトルxとyの内積を計算する問題を考えます。

d=i=1nxiyi

xiyi

たとえば、2つのベクトルの積を最初に次のように計算できます。

d=((x1y1)+(x2y2))+(x3y3)

そして次に

d=(x1y1)+((x2y2)+(x3y3))

これはどのように起こりますか?2つの可能性があります。

  1. 並列コアでのマルチスレッド計算。最近のコンピューターには通常、2、4、8、またはそれ以上のプロセッサーコアがあり、並列で動作します。コードが複数のプロセッサでドット積を計算するために並列スレッドを使用している場合、システムのランダムな摂動(たとえば、ユーザーがマウスを動かし、プロセッサコアの1つがドット積に戻る前にそのマウスの動きを処理する必要がある)追加の順序が変更されます。

  2. データとベクトル命令の整列。最新のIntelプロセッサには、(たとえば)一度に浮動小数点数を操作できる特別な命令セットがあります。これらのベクトル命令は、データが16バイト境界に整列されている場合に最もよく機能します。通常、ドット積ループは、データを16バイトのセクションに分割します(一度に4つの浮動小数点数)。2回目にコードを再実行すると、データがメモリの16バイトブロックと異なる方法で整列され、追加が行われます。異なる順序で実行され、結果として異なる答えが返されます。

コードをシングルスレッドとして実行し、すべての並列処理を無効にすることで、ポイント1に対処できます。メモリブロックを整列するためにメモリ割り当てを要求することにより、ポイント2をアドレス指定できます(通常は、-alignなどのスイッチを使用してコードをコンパイルすることでこれを行います)。で。

このIntelのドキュメントでは、Intel Math Kernel Libraryを使用した結果の再現性が失われる可能性のある問題について説明しています。 インテルのコンパイラーで使用するコンパイラースイッチについて説明している、インテルの別のドキュメント


コードがシングルスレッドで実行されていると思います。あなたはおそらくあなたのコードをよく知っていますが、マルチスレッドで実行されるサブルーチン(たとえば、BLASルーチン)を呼び出しているとしても、私は驚かないでしょう。使用しているライブラリを正確に確認する必要があります。システム監視ツールを使用して、CPU使用率を確認することもできます。
ブライアンボーチャーズ2016

1
または、述べたように、FFTWライブラリ...
クリスチャンクラソン

@BrianBorchers、ありがとう。浮動小数点加算の非連想的な性質から生じるランダム性の例は、啓発的です。クリスチャン・クラソンは、数値の大きさを考えると、私のモデル出力が意味があるかどうかについて二次的な問題を提起しました-彼が正しい(そして私が彼を正しく理解している)場合、それは主要な問題である可能性があるので、私は今それを調べています。
boxofchalk1

2

上記のFFTWライブラリは、非決定的モードで実行される場合があります。

FFTW_MEASUREまたはFFTW_PATIENTモードを使用している場合、プログラムは実行時にチェックし、どのパラメーター値が最も速く機能し、プログラム全体でこれらのパラメーターを使用します。実行時間は明らかに少し変動するため、パラメーターは異なり、フーリエ変換の結果は非決定的です。確定的なFFTWが必要な場合は、FFTW_ESTIMATEモードを使用します。


1

マルチコア/マルチスレッド処理シナリオが原因で式の用語の評価順序の変更が発生する可能性が高いのは事実ですが、(たとえ長い目で見ても)何らかのハードウェア設計上の欠陥がある可能性があることを忘れないでください。Pentium FDIV問題を覚えていますか?(https://en.wikipedia.org/wiki/Pentium_FDIV_bugを参照してください)。少し前に、PCベースのアナログ回路シミュレーションソフトウェアに取り組みました。私たちの方法論の一部には、ソフトウェアの夜間ビルドに対して実行する回帰テストスイートの開発が含まれていました。私たちが開発したモデルの多くでは、反復法(例:Newton-Raphson(https://en.wikipedia.org/wiki/Newton%27s_method))とRunge-Kutta)は、シミュレーションアルゴリズムで広く使用されていました。アナログデバイスでは、電圧や電流などの内部アーチファクトが非常に小さい数値で発生することがよくあります。これらの値は、シミュレーションプロセスの一部として、(シミュレーションされた)時間の経過とともに段階的に変化します。これらの変化の大きさは非常に小さい可能性があり、私たちが頻繁に観察したのは、そのようなデルタ値に対する後続のFPU操作がFPUの精度の「ノイズ」しきい値に隣接していることでした(64ビット浮動小数点は53ビットの仮数、IIRC)。それと、「PrintF」ロギングコードをモデルに導入しなければならないという事実と相まって、デバッグを可能にするために(ああ、古き良き時代!)、実際には散発的な結果が毎日保証されます!だから何' これはどういう意味ですか?このような状況では違いが生じることを期待する必要があります。最善の方法は、それらをいつ/どのように無視するかを決定する方法(大きさ、頻度、傾向など)を定義して実装することです。


ジム、洞察をありがとう。どのような基本的な現象がそのような「内部のアーティファクト」を引き起こすのかについてのアイデアはありますか?電磁干渉もあるのではないかと思ったのですが、かなりのビットも影響を受けるのでは?
boxofchalk1 2016

1

非同期操作からの浮動小数点の丸めが問題になる可能性がありますが、もっと平凡なものだと思います。初期化されていない変数を使用することで、決定論的なコードにランダム性が追加されます。デバッグモードで実行するとすべての変数が宣言時に0に初期化されるため、これは開発者が見落としがちな一般的な問題です。デバッグモードで実行していない場合、変数に割り当てられたメモリは、割り当て前のメモリの値と同じです。最適化としての割り当てでは、メモリはゼロになりません。これがコードで発生している場合は、簡単に修正できますが、ライブラリコードでは修正は簡単です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.