Design: NaNビットが完全に決定論的でない理由を文書化する

作成日 2016年03月22日  ·  15コメント  ·  ソース: WebAssembly/design

(私は現在提唱していません。これは、情報に基づいた決定を下し、理論的根拠のために資料を収集するためです。)

Nondeterminism.mdを見ると、NaNビットの部分が際立っています。 もちろん、スレッドが競合したり、リソースが使い果たされたり、機能が追加されたりする可能性があります。 これらは、全体的な設計の結果です。 しかし、NaNビット? VMはもう少し賢く、これを処理できますか? 2つの問題があります:

演算が複数のNaNオペランドを取得する場合、どれが伝搬しますか?

IEEE 754はこれを指定しないままにしますが、少なくともx86、ARM、およびPowerはすべて「最初の」オペランドを選択するため、これは妥当な選択です。

ただし、wasmレベルで選択を修正すると、wasm実装は浮動小数点の加算と乗算に対応できなくなります。これは、命令が入力の1つを壊してしまうVEXx86より前の場合に役立つ最適化である場合があります。 また、LLVMベースのVMを使用している人は誰でも、加算と乗算がwasmで可換ではないことを教える必要があります。

これらは目立たないものではないと合理的に主張することができるので、強い欲求があれば、この問題は理論的に修正可能です。

演算がNaNを生成し、NaNオペランドがない場合、符号ビットは何ですか?

x86は1を使用し、ARMは0を使用します。

これを修正する最も簡単な方法は、すべての浮動小数点演算の後に正規化することです。 これは実行可能ですが、伝播するNaNオペランドがある場合、0と1の両方が有効な値になる可能性があるため、NaNの結果をチェックして正規化するだけでは不十分です。 NaNの結果とNaNオペランドの欠如をチェックしてから、正規化する必要があります。

_every_操作後の正規化は非常にコストがかかります。 もう1つのオプションは、計算の「エスケープ」ポイントで正規化することです(正規化パスは、正しいNaN出力を決定するために、基本的に計算のストリーム全体を再生する必要があります)。 これは改善ですが、それでもかなりのオーバーヘッドが追加される可能性があります。

別の可能な実装は、無効な例外のマスクを解除し、NaNが生成されるたびにトラップを取得してから、正規化を実行して戻ることです。 欠点には、デフォルト以外のCPUモードを使用することや、無効なものが多数生成されると非常に遅くなることが含まれます。

残念ながら、これらのアプローチには重大な欠点があります。 他のアイデアが浮かび上がるか、非常に強い欲求がない限り、これを修正するのは難しいようです。


もう1つ注意すべき点は、NaNビットを誤って観察することは難しいため、これは一般に移植性に関する大きな懸念事項ではないということです。

他の誰かが追加する考えがありますか?

clarification floating point

最も参考になるコメント

決定を下す前に、これによるパフォーマンスへの影響を定量化したいと思います。 私たちのツールチェーンはまだ未成熟で、現時点では優れたパフォーマンス測定を行うことができないと思います(これの方法には長い極があります)。 言い換えれば、「1000カットによる死」を避けたいのです。

全てのコメント15件

もう1つ注意すべき点は、NaNビットを誤って観察することは難しいということです。

それらを観察できないようにする方法、または少なくとも符号ビットを作成する方法はありますか?

IMOのNaNの非決定性は、ソフトウェアがそれらを気にすることは非常にまれであるため、そのままにしておく必要があります。 パフォーマンスを犠牲にして決定論的にする場合はどうなりますか?

NaNビットを観察する方法のリストは次のとおりです。

  • reinterpret変換
  • storeを線形メモリにロードし、異なる解釈でビットをロードします(またはビットを外部で観察させます)
  • インポートされた関数のcallに引数を渡すか、エクスポートされた関数から値を返します
  • copysign非NaNへの符号ビット

VMは、これらのそれぞれの前に正規化コードを挿入できます。 これが、前述の「エスケープポイントで正規化する」という考え方です。 store

@qwertie私はここで説明するように、仕様に含まれていない場合でも、完全に決定的にする方法があります。

@qwertieの利点には、移植性がわずかに向上する(たとえば、IEEE 754 totalOrder関数を不適切に使用するコードが世界に存在する)、再現性がわずかに向上する、複数のノード間で対称計算を行う場合の不変条件が強化されるなどがあります。

store

この場合、常に符号値を指定できますか? それがmemに配置された場合、ちょうど1にするように?

@wandererこれは基本的に、正規化に伴うものです。値がNaNであるかどうかを確認し、そうである場合は、修正を適用します。 これは通常、ホットパス上の追加の比較と分岐です。

また、この号で言及されているオプションのベンチマークを行っていないことも付け加えておきます。 誰でもここに追加できるベンチマークは大歓迎です。

私は、パフォーマンスを優先する現在の非決定論のレベルに強く傾倒します。
私は実際、wasmがNaNの仮数ビットを厳密に定義していることに少し驚いていました。
これは今日のプロセッサには十分かもしれませんが、将来のプロセッサがどうなるかを知っている人はそうです。

wasmが高級言語から生成されている場合、そのトランスレータは、たとえば-ffast-mathのように、FPセマンティクスのレベルを制御するオプションを提供できます。 次に、トランスレータは、nansを目的の形式に強制するために必要な追加のwasm修正を挿入できます。 そのために、isNanまたはnormalizeNan演算子を提供できますが、現在はこれを推奨していません。

つまり、これを「修正」するのは、より高いレベルのツールであるIMOに任せるのが最善です。

+ 1 @ mbodart。 編集:ああ、実際には+1のものがあります:)。 ところで、私は個人的にWasm =世界の支配だと思います。したがって、将来のプロセッサはそれに敵対することはありません。

決定を下す前に、これによるパフォーマンスへの影響を定量化したいと思います。 私たちのツールチェーンはまだ未成熟で、現時点では優れたパフォーマンス測定を行うことができないと思います(これの方法には長い極があります)。 言い換えれば、「1000カットによる死」を避けたいのです。

@jfbastienに同意します。 セマンティクスをいじくり回す前に、まずパフォーマンスへの影響を定量化する必要があります。

明確にするために、私は現在、ここでの変更を提唱していません。 理論的根拠の資料を集めています。 NaNビットの非決定性が突き出ており、説明が必要です。 そして、私が知る限り、これらのビットを非決定論的にすることによるパフォーマンスへの影響を定量化した人は誰もいません。

両方のオペランドがNaNの場合、ARMv8は常に最初のオペランドを伝搬するとは限りません。 ルールは次のとおりです。

  • 一方のオペランドがクワイエットNaNで、もう一方がシグナリングNaNの場合、シグナリングNaNを伝搬します。
  • それ以外の場合は、最初のオペランドを伝搬します。

仕様を読んでいると、これはARMv8のAarch32モードとAarch64モードの両方に当てはまります。

この動作は、どちらの場合も第1オペランドを伝搬するSSEとは異なります。

どちらのアーキテクチャも、伝搬する前にクワイエットビットを設定することにより、sNaNをqNaNに変換します。

@stoklund良い場所です! 2番目のNaNがシグナリングしている場合、ARMが最初のNaNを選択することは発生しないことを見逃しました。 それは私が上記の複数のNaNの場合のためにレイアウトした戦略を複雑にするでしょう、それで私達は理論的根拠の中でそれを言及することができます。

上記を要約した特定のテキストを提案するために、#973を作成しました。

このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

konsoletyper picture konsoletyper  ·  6コメント

arunetm picture arunetm  ·  7コメント

aaabbbcccddd00001111 picture aaabbbcccddd00001111  ·  3コメント

jfbastien picture jfbastien  ·  6コメント

beriberikix picture beriberikix  ·  7コメント