ARMの平方根(2)
ふと思った。
s = (x / s + s) >> 1;
この部分、除算を使っているんだけれども、ARMの命令には除算命令がない。
どうやって計算してるんだろう?と思ってアセンブラを吐き出してみると……。
BL __rt_udiv
除算演算ライブラリを使ってるよ(ノ∀`)アチャー
こりゃ考えるまでもなく遅いな……。
昨日のURLに、
#define iter1(N) \ try = root + (1 << (N)); \ if (n >= try << (N)) \ { n -= try << (N); \ root |= 2 << (N); \ } uint32 sqrt (uint32 n) { uint32 root = 0, try; iter1 (15); iter1 (14); iter1 (13); iter1 (12); iter1 (11); iter1 (10); iter1 ( 9); iter1 ( 8); iter1 ( 7); iter1 ( 6); iter1 ( 5); iter1 ( 4); iter1 ( 3); iter1 ( 2); iter1 ( 1); iter1 ( 0); return root >> 1; }
ってのがあるんだけど、これなら1bitの収束が4クロックなので4*16=64クロックで、他の部分を入れて70〜80クロックぐらいと予想。
誤差の計算もしてみたけど、ニュートン法で5回計算したのと比較しても、ニュートン法の方が1だけ大きい場合が時々あったぐらいで、他は全て同じ。
これはかなり良さげ。
気が向いたら実機での速度を測ってみるかな(*´ω`)
追記:
BREW3.1のとある機種で、
uint32 s = 0; uint32 t; while( true ){ sqrt( s ); t = s; s += 1999; if( t > s ) break; }
こんなプログラムを組むと、
ニュートン法 → 5290ms bitごとに収束 → 880ms
になりました。
やっぱりARMは分岐に強いですね。
追記2:
ARMだから速いと思ったら、どうやら違うようです。
PC(Pen4 2.8GHz)で測定してみると、
ニュートン法 → 313ms bitごとに収束 → 47ms
でした。
除算って結構大きい処理なのね(;´Д`)