HLSLで浮動小数点数のアトミック演算

Direct3D 11のシェーダでバッファや共有メモリに対してアトミック演算を行うことがありますが、HLSLのAtomic演算(Interlocked関数)はuintかint型を対象としていて、floatには対応していません。今回は、無理やりInterlocked関数を使って浮動小数点数の比較を行ってみました。

■方法

実は、浮動小数のビットの並びをそのまま整数とみなして比較することができます[参考文献1]。HLSLではasuint() / asint()関数で浮動小数のビット列のまま整数に変換できます。

ただし、負の小数との比較をする場合は二の補数をとる必要があります。また、非正規化数や非数は誤った比較結果を引き起こします。

■検証

適当に乱数を振って0.0から100.0までの浮動小数点数を作り、2つの配列に格納し、アトミック演算で小さい方の値を求める(HLSLのInterlockedMin()に相当する)プログラムをC++ AMPで書きました(C++ AMPのコードはコンパイラによってCompute Shaderに変換されます)。

CPUでも同じ計算を行い、誤りがあれば異常終了しますが、こちらの環境では数回実行して全て正常終了しました。

ソースコードはこちら

(こちらの環境では、しばしばコンパイラがメモリ不足でビルドに失敗する現象が起きました。何回かビルドし直してみてください。)

本当にアトミック演算が実行されているのかを確認するため、Shader Assemblyを確認しました。


unsigned int iv = *reinterpret_cast<unsigned int*>(&v);
0x000000EC iadd [precise(y)] r0.y, r0.x, cb1[7].x
0x0000010C ishl [precise(y)] r0.y, r0.y, l(2)
0x00000128 ld_raw_indexable [precise(y)](raw_buffer)(mixed,mixed,mixed,mixed) r0.y, r0.y, u0.xxxx
concurrency::atomic_fetch_min(&viewResult(threadIdx), iv);
0x0000014C iadd [precise(x)] r0.x, r0.x, cb1[16].x
0x0000016C ishl [precise(x)] r0.x, r0.x, l(2)
0x00000188 atomic_umin u1, r0.x, r0.y
0x000001A4 endif
0x000001A8 ret

atomic_uminという命令があることから、確かにアトミック命令が使われていることが分かります。ただしDebugビルドではCPUエミュレーション実行になるので注意してください。

ちなみにatomic_fetch_min()をatomic_fetch_max()に置き換えると、HLSLのInterlockedMax()に相当する処理が行えます。もちろんこの場合も正常終了することを確認しています。

asfloat()をC++ AMPで表現するには、reinterpret_castすれば良いようです。HLSLにはポインタの概念がないのに上手く裁けるC++ AMPすごい。

■余談

アトミック加算に限れば、CUDAはCompute Capability 1.1以降でatomicAdd(float)をサポートし、OpenGLもGL_NV_shader_atomic_float拡張でimageAtomicAdd(float)をサポートしています。HLSLは非サポートです。

■参考文献

  1. How to optimize for the Pentium family of the microprocessors (In Japanese)

 

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中