pow(x, 2) vs (x * x) どっちが速いか

プログラムを書いていて少し気になることがありました。べき乗を求めるpowという関数がありますが、二乗の場合はpow(x, 2)と書くのと(x * x)と書くので、どちらが速く実行されるのでしょうか。早速ベンチマークを取ってみようということになりました。

僕の予想としては、コンパイラが最適化してくれるおかげでどちらでも同じ結果が得られるだろうという予想です。皆さんはどうでしょうか。結果は続きをどうぞ。

ベンチマークの環境は以下のとおりです。

  • コンパイラ: M$ VC2005 Express Edition
  • コンパイラオプション 主に/O2 /arch:SSE2(その他はmakefileVC8参照)
  • ソースコードはpow2_test.cpp
  • 開発、実行環境はThinkPad X60s (Core Duo L2400 1.66GHz + RAM 2GB) + M$ Windows XP SP2

ベンチマークのとり方に問題がある可能性がありますが、結果は(x * x)の圧勝でした。

計測結果
項目時間 [cycle]
pow(x, 2)761990720
(x * x)83377840

コンパイラはそこまで賢くないのでしょうか? アセンブラpow2_test.asmを見るとtest1(=> pow(x, 2))とtest2(=> (x * x))で違うコードが吐かれていることが確認できます。今度gccでも試してみようと思います。

ちなみに時間計測について参考にさせて戴いたのは、WATARU's MEMO 『RDTSC 命令 --凄いぞ、64ビットカウンター!--』『- read-time stamp counter (RDTSC)命令の利用方法 -』です。ありがとうございました。

March 02, 2008 23:37 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク

コメント

私も気になったのでVisual C++ 6.0で計測してみました。
結果としては、
・powの片方にでも変数が入ると展開してくれない
・両方定数(constでも可)なら展開する
・volatileが付くと最適化防止がかかるので展開してくれない
といったところでしょうか。
そこまで深いチェックはしてないので間違ってるかも知れませんが・・・。まぁ。予想通りではあります。

Posted by: hajime : March 3, 2008 01:47 AM

double pow(double x, double y)なので(ですよね?)、pow(x,2)はpow(x,2.0)とキャストされて渡されるわけで、これをx*xと解釈するのはむずかしい気がします。

というわけで普段x*xを使っているのですが、
例えば、(a+b+c)^2が欲しいときに、
(a+b+c)*(a+b+c)とやるか、
x=a+b+c;としておいてx*xとやるかはよく迷います。
後者が速い気がしますが、めんどうなので。
それは最適化してもらえるのかな。

Posted by: catacly : March 3, 2008 01:51 AM

>hajimeさん
VC6での検証、ありがとうございます。両方定数なら、というのが曲者ですね。片方が定数の場合(今回は2のみ)の展開もサポートしてくれると嬉しいよね、というのがこのお題の本質でした。

>cataclyさん
確かに標準的なC言語ではpow(double, double)となっていますが、C++ではオーバーロード(引数の型や数が別なら同一名でも複数の定義が可能)の機能があるので、pow(double, int)というのが隠れてあったりします。その証拠にアセンブラ出力を見ると、今回は内部的には_Pow_intというテンプレート関数が呼び出されていることが確認できると思います。
(x * x)の件ですが、xが簡単の場合は最適化がかかると思いますが、複雑な場合は怪しいですね。これも試してみる価値があると思います。

Posted by: fenrir : March 5, 2008 12:26 AM

コメントする