開発者ミーティングでは、ごく一部ながらActiveBasicのソースコードを拝見する機会もありました。その中で印象的だったのは、32ビットのコードにおける64ビット乗除算です。現在のABでは共に関数呼出となりますが、その関数は直に機械語で(ABコンパイラのソースコード中に)記述されていました。たしか山本さんは、8ビットずつ処理しているというようなことを仰っていました。

そこで疑問が生じました、なぜ32ビットずつ処理しないのかと。私もアセンブリ言語の心得が全く無いわけではありません。自分で試しに書いてみることにしました。

a, bを64ビット符号無し整数で表せる範囲内の数とします。その上位32ビット、下位32ビットをそれぞれah/bh, al/blと表します。さらにR = 2 ^ 32とすると、その乗算は次のようになります。

a * b
= (ah * R + al) * (bh * R + bl)
= ah * bh * R ^ 2 + ah * R * bl + bh * R * al + al * bl
= ah * bh * R ^ 2 + (ah * bl + bh * al) * R + al * bl

64ビット同士の乗算の結果を表すには最大128ビット必要ですが、ここでは下位64ビットさえ求められれば十分です。それにはah * bl + bh * alの下位32ビットにR (= 2 ^ 32)をかけたものとal * blを足せば良いはずです。ABのコードで模式的に表すとこういうことになります。

Function Mul64x64to64(a As QWord, b As QWord) As QWord
	Dim ah As DWord, al As DWord, bh As DWord, bl As DWord
	ah = HIDWORD(a)
	al = LODWORD(a)
	bh = HIDWORD(b)
	bl = LODWORD(b)
	Mul64x64to64 = ((al * bl + bh * al) < < 32) + (al As QWord * bl As QWord)
End Function

HI/LODWORDは64ビット整数から上位・下位32ビットを取り出します。HI/LOWORDと同じような感じです。ところで、al * blでは結局64ビットで乗算していますが、これは32ビットの整数同士の演算は結果も32ビットになるというABの仕様のせいです。我々が普段使っているx86 CPUでは32ビット×32ビット→64ビットになる整数乗算の命令を持っているので、アセンブリ言語からそれを使います。NASMやMASMもあるのですが、Visual C++のインラインアセンブラを使うことにします。ソースコード

さて実際にテストしてみます。

速度はAB5 CP3の乗算演算子による64ビット演算と比べても遜色ありません。ソースコード中のMemSizeを1024 * 1024 * 32にして実行しましたが、私のPCではおよそ乗算演算子・MyMul共に350-450ミリ秒となります。

32ビット同士の演算へ分解してもうまくいくことがわかって自己満足したところで今回は終わりです。なお、ABが64ビット乗算をどのようにしているかは、結局まだ見ていません。今度暇があったら逆アセンブルしてみてみようと思います。

明らかにおかしい点があるなどなど疑問があればコメントへお願いします。


スポンサード リンク

この記事のカテゴリ

  • ⇒ 第一回開発者ミーティング – (3) 64ビット乗算の謎