!!!数値演算処理のベンチマーク(Win/Mac/IntelMac) 2006/02/21 2006年1月に、IntelMacが市場に出始めました。 数値演算機能の性能を調べてみたかったので調査を行い、 その調査内容・結果を記述しています。 !!検証マシン 以下の3台のマシンにてベンチマークを行っています。 !WindowsXP (Pentium4 2.8GHz) Windows(Windows XP SP2)マシンです。デスクトップマシン。 !MacOSX 10.4.3(G4 800MHz) PowerPC G4のiBookです。 !MacOSX 10.4.4(Intel CoreDuo 1.83GHz) IntelMacマシンです。ネイティブなUniversal Binaryによる測定と、 PowerPCエミュレーションである「Rosetta」の両方をチェックしました。 !!検証内容 以下の処理を、それぞれ10000 * 10000回繰り返して実行し、 そのときの経過時間をミリ秒単位で計測しました。 なお、同等の処理を数回行い、計測による誤差がないか確認も 行っています。 *カラの関数呼び出し *int型の整数四則演算(加算、減算、乗算、除算) *float型の単精度浮動小数点四則演算(加算、減算、乗算、除算) *ANSI標準の算術関数(fabs、fmod、sqrt、1.0/sqrt(逆数平方根)、cos、sin、pow) *処理を高速化できるようにした算術関数(fabs、fmod、sqrt、1.0/sqrt) *3Dベクトル演算(ベクトルの長さを求める、ベクトルの正規化) それぞれを関数呼び出しを通して一処理を行っています。これを繰り返し呼び出しています。これにて、最適化による繰り返し実行が飛ばされないように、正確に 計測ができるようにしています(できている、かな?)。 四則演算や算術関数を呼び出す時間'''だけ'''を計測できるようにするため、 計測後からカラの関数呼び出しに要した時間を引くようにしています。 int/floatの四則演算は、単なる「+ - * /」の計算です。 アセンブラ化すると、1命令であらわされる極々小さな処理です。 fabs/fmod/sqrt/cos/sin/powは、ANSIのmath.hの命令を呼んでいます(与える引数は定数を入れてます)。 「1.0/sqrt」は逆数平方根の計算です。 これは、ベクトルを正規化する場合などによく使われますのであえて こちらに加えておきました。ソース上では以下のようになります。 double mb_Bench_rsqrt(double val) { return (1.0 / sqrt(val)); } 「高速化できるようにした算術関数」というのは、以下のようにソースを記述しています。本当に高速化できるかは環境依存(^_^;;。 !fabsを高速化したつもり ifで比較して絶対値を返すようにしてます。 double mb_Bench_fast_fabs(double val) { return (val < 0) ? -val : val; } !fmodを高速化したつもり valをval2で割ったときのあまりを返します。 double mb_Bench_fast_fmod(double val, double val2) { return (val - val2 * ((int)(val / val2))); } !逆数平方根の高速化 近似で高速化します。が、これはソースは非公開。 !平方根の高速化 近似で高速化します。が、これはソースは非公開。 基本は、逆数平方根を求めるほうが負荷がかかりません。 それから、乗算を1回行って平方根が求まります。 !ベクトル演算(長さを求める) typedef struct { float x, y, z; } MB_VECTOR3; という構造体のXYZ要素を持つベクトルの長さを求めます。 内部的に平方根計算(sqrt)が入ることになります。 4パターンの求め方を行っています。 1つめは、 float mb_VectorLength(const MB_VECTOR3 vec); と定義されており、引数に元値をそのまま入れるものです。 これは、関数が呼ばれるときにメモリコピー(代入)が起こることになります。 2つめは、 float mb_VectorLength2(const MB_VECTOR3 *pVec); と定義されており、引数はポインタ渡しです。 3つめは、 float mb_VectorLength3(const MB_VECTOR3 &vec); と定義されています。これも実質はポインタ渡しと同じになります(最近のはやりか?)。 4つめは、 float mb_VectorLength4(const MB_VECTOR3 &vec); で上記の3つめと同じではありますが、内部の平方根計算にて 高速化用のコードを呼んでます。 !ベクトル演算(ベクトルを正規化する) 内部的に逆数平方根計算が入ることになります。 2パターンの求め方を行っています。 1つめは、ANSIのsqrt命令を使い、「1.0 / sqrt」として 逆数平方根を求めています。 2つめは、高速化用の逆数平方根計算を呼んでいます。 !!ベンチマークでのコンパイル環境 Windowsは、VisualC++ .NET2003にてRelease(最適化あり)ビルドを 行いました。 OSXは、Xcode2.1にてRelease(最適化あり)ビルドを行いました。 なお、UniversalBinaryでビルドしています。 !!ベンチマークソース&プログラムのダウンロード WindowsおよびOSXでのプロジェクト、ソース、実行ファイルを zip圧縮しています。ソースはWindows/OSX共に共用です。 ダウンロード(63KB):{{ref MathBench_src_20060221.zip}} !注意点!! 高速化のためのsqrt/rsqrt(逆数平方根)関数は、ソース上ではあえて ANSIのものを入れるようにしています(バイナリは高速化処理を行っています)。 私が使っているコア部分のコードが入ってますので、この部分だけは 非公開とさせてください。 実行時は、WindowsはDOS窓で実行するようにしてください。 OSXではTerminalで実行できますが、文字コードはShiftJISにするようにしてください (TerminalデフォルトはUTF-8のエンコードになってます)。 !!!計測結果 !!結果を数値で比較 見難くて申し訳ありませんが、以下のような結果に。 単位はミリ秒です。 {{ref_image benchmark_20060221.png}} 比較するOS・クロック数がまちまちですので、いまいち分かりにくいですね。 この部分は、後にグラフと考察で解説していきます。 !!四則演算を比較する 縦軸の単位はミリ秒です。棒が短いほど速い、ということになります。 {{ref_image benchmark_20060221_00.png}} ぱっと見で分かることは、'''「除算(割り算)が遅い」'''ということです。 これは、Win/OSX両方ともです。かつて調べた「http://www001.upp.so-net.ne.jp/y_yutaka/labo/math_algo/calcbench.html」と、 OSXでの結果が違いますね。除算がOSXでも弱くなってる・・・。 OSXのバージョンとXcodeの違いでしょうか。 ただ、OSX(Intel)のRosettaがなぜか速くなってます(最適化で変に飛ばされたのかな?)。この部分は、UniversalBinaryの恩恵はあまりないような気がします。 整数にしろ浮動小数点にしろ、PowerPCはもともと優秀だった(?)というのもあり、 Intelになっても差が出る部分ではないのかもしれません。 また、加減乗算は、軒並みUniversalBinaryよりもPowerPC実行のほうが速い結果となりました。 除算に関しては速くなってますね。 !!ANSIの算術演算を比較する 今度は算術演算命令を比較です。 {{ref_image benchmark_20060221_01.png}} これは驚くべき比較結果。CPUクロック数を入れないとしても劇的です。 個々に見ていきます。 !fabs ゴミのようです(^_^;;。単なる符号反転だけですので、別段時間がかかってない ということみたいですね。 ただ、数値で見るとUB版はPowerPC(G4)よりは8倍以上遅くなってます。 これだけは明らかに「'''UBで遅い!'''」といえそうです。 !fmod 除算のあまりを求める命令ですが、これは大きな差が出てます。 CPUクロック数を考えないとすると、 PowerPC(G4 800MHz)とIntelMac(Intel CodeDuo 1.83GHzのUB)での倍率は、 約25倍速です。 !sqrt 平方根もIntelMacで速くなりました。が、クロック比較ではWindowsほどではないようです(Windowsのほうが、相変わらずバク速ということか)。 PowerPCとIntelMacの比較は約4.6倍です。 !1.0 / sqrt 逆数平方根は、除算とsqrtの2つを行いますが、全体的にはUBにて速度アップしたと いえそうです。 !cos /sin Win/OSX双方で、わずかにcosのほうがsinよりも速いですね。 UBで速くはなってますが、クロック比較ではWindowsほどではないかな。 !pow クロック比率では、G4と差があるのかなぁ、微妙です。 少なくとも、Windowsよりは遅いですね。 で、IntelMacでのRosetta動作では、ですが、 G4マシンと比べてもとんとんか遅くなってます。少なくとも、UBと 比べると明らかに低下していると言わざるを得ません。 !!自前実装して高速化する fabs/fmod/sqrt/rsqrt(逆数平方根)を一部自前実装して 高速化を試みてみました。 そのときのベンチマークのグラフです(数値は前述した表を参照)。 {{ref_image benchmark_20060221_02.png}} !fabs Windowsの場合は、ANSIのfabsに頼ったほうが速いです。 PowerPCはANSIも自前実装もほぼ同じ、 UBもほぼ同じ、 RosettaはANSIでのほうが速い結果となりました。 ただ、PowerPCとUBでは、いずれもPowerPCのほうが速いですねぇ。 ということで、素直にANSIのfabs関数を使ったほうがよさそうです。 !fmod これは性格に結構差がありました。 PowerPCのfmodがかなり遅いのに対して、自前実装だとかなり改善されてます。 ANSIと自前実装の比較は、Windowsは2.7倍速、PowerPCは8.3倍速!!、 UBは0.8倍速、Rosettaは17.5倍速、となりました。 UBの場合のみ、ANSIに頼るのがいいかもしれないです。 !sqrt Windows/UBはANSIに頼ったほうが速い、 PowerPC/Rosettaは自前実装の方が速い、となりました。 これは、IntelMacにて平方根計算が改善されたからかもしれないですね。 PowerPCは平方根に弱いようです。 !rsqrt(逆数平方根) CPUとしては、逆数平方根のほうが効率が良かったりします。 Windowsの場合はわずかに自前実装のほうが速い、 PowerPCは自前実装のほうが2.1倍速、 UBはほぼ同じ、 Rosettaは自前実装のほうが3.0倍速。 これは、すべてにおいて自前実装のほうがいいかも? ただ、SSE2命令ではすでにrsqrt命令がありますので これが使えるなら使ったほうがいいかもしれません(IntelMacも同様です)。 !!ベクトル演算 {{ref_image benchmark_20060221_03.png}} これは、明らかにUBで効率アップしているのが分かります。 もっともUB対応で高速化できている部分かもしれません。 !ベクトルの長さを求める UBに注目してください。 1つめと2-4つめで結構な差があります。これは、「引数に構造体の 値を渡したかポインタで渡したか」の違いです。 これを見て分かるのは、'''引数で値渡しをすることを極力控える''' ということです。 4つめのベクトルの長さ計算は、平方根計算を自前実装してます。 PowerPC/Rosettaの場合は、4つめの方法がいいかもです。 Windows/UBは、ANSIのsqrtで長さを求めるようにします。 !ベクトルを正規化する これは、逆数平方根計算をANSIを使って「1.0 / sqrt」とするのが 1つめで、自前で行うのが2つめです。 Windows/UBはANSIを使うほうがよい、PowerPC/Rosettaは自前実装が いいかも。 !!!考察 と、いろいろテストしてみましたがOS(というかCPU)によって ずいぶんと結果が違うのが分かるかと思います。 それぞれの演算・関数ごとに得手不得手がある感じがします。 まとめると、 *加減乗算では、IntelMacの恩恵は薄い(逆に遅くなっている)? *除算では、PowerPCに比べてUB対応で速くなっている。 *fabsはUBは遅い? *fmodはUBでバク速 *sqrtはWindowsがバク速(UBは普通、PowerPCは劇重) *ベクトル計算などはUB対応でかなりの高速化 *関数に値を渡すときなどはポインタ渡しを心がけること となりました。 PowerPCでのボトルネックであるsqrtがUBにて改善されたことで、 全体で高速化に貢献してるのかな、という気がします。 また、ここではテストしてませんが描画部分も高速化している ような気がしますので、もしかしたらメモリ転送も テストしてみてもいいかもしれません。 もちろん、これだけではアプリケーション全体の 速度アップは判断できません。 IntelMacの「CoreDuo」を生かす、SSE2を生かす、 Windowsのハイパースレッド対応などでもっと最適化できることと思われます。 少なくとも、算術演算命令が軒並み速度アップしていますので、 これだとたしかに今までのMacとは違うスピードをかもし出せそうですね。