x87 FPU で厳密な単精度演算を行うコスト

senna_hppの日記より.

DirectXから入ったので、浮動小数点演算にはfloat型演算を使うようになったのですが、実際にはdouble型の方が速いそうなのでその確認のための検証実験。

(中略)

typeadd(ms)sub(ms)multi(ms)div(ms)
double203.6201.95.65.4
flaot913.4912.9674.0675.1
double(template)201.1201.35.15.4
float (template)913.8915.0680.2675.5
double(template specialization)201.7201.56.54.7
float(template specialization)913.6912.2674.0674.2

結果としてはdouble型が完全勝利と言えるでしょう。

ただし、PS2などはfloat型演算の方が速いので、処理系やコンパイラによって常にこのような結果になるとは限りません。

試しに手元の Visual Studio 2005 でもコンパイルさせてみましたが,ディスアセンブル結果を見てると,何となく状況が読めてきたり.Release ビルドのデフォルト値から想像するに,恐らくコンパイルオプションの「浮動小数点モデル」が「Precise (/fp:precise)」になっているのかと思います.

// double 版 (速い)
        for(int i=0;i<N;++i) a1 += a2;
00401069  sub         eax,1
0040106C  fadd        st(3),st
0040106E  fadd        st(2),st
00401070  fadd        st(1),st
00401072  fadd        st(3),st
00401074  fadd        st(2),st
00401076  fadd        st(1),st
00401078  fadd        st(3),st
0040107A  fadd        st(2),st
0040107C  fadd        st(1),st
0040107E  fadd        st(3),st
00401080  fadd        st(2),st
00401082  fadd        st(1),st

// float 版 (遅い)
        for(int i=0;i<N;++i) a1 += a2;
004013AD  fld         dword ptr [esp+20h]
004013B1  fadd        st,st(1)
004013B3  fstp        dword ptr [esp+20h]
004013B7  fld         dword ptr [esp+24h]
004013BB  fadd        st,st(1)
004013BD  fstp        dword ptr [esp+24h]
004013C1  fld         dword ptr [esp+28h]
004013C5  fadd        st,st(1)
004013C7  fstp        dword ptr [esp+28h]
004013CB  fld         dword ptr [esp+20h]
004013CF  fadd        st,st(1)
004013D1  fstp        dword ptr [esp+20h]
004013D5  fld         dword ptr [esp+24h]
004013D9  fadd        st,st(1)
004013DB  fstp        dword ptr [esp+24h]
004013DF  fld         dword ptr [esp+28h]

詳細については以前の日記例の記事で書いたので省略しますが,問題の float 版では,連続して fadd していくと指数部が 80-bit 精度で計算され続けてしまうため,毎回メモリに書き出して読み直すことで単精度での完全丸めを行っているようです*1
見るからに悲惨ですが,こうやってパフォーマンス差と一緒に見せられると確かにインパクトありますねぇ.
ちなみにこのテストコードで「浮動小数点モデル」を「Fast (/fp:fast)」にすると,概ね float の方が速いという結果になるようです*2



CodeZine の記事用に作ったけど結局使わなかった図もついでに貼ってみたり.

*1:store-reload で FP-strict な単精度演算が実現できることについては,首藤氏の『厳密な浮動小数点演算セマンティクスの Java 実行時コンパイラへの実装』参照のこと

*2:追記.とはいえほとんど差はない