2048*2048[px]の画像に対してJavaScriptでフィルタをかけることを想定して、2048*2048*4回の基本演算の所要時間と、2048*2048[px]のCanvasに対するgetImagePixelsとputImagePixelsの速度を計測しました。テストプログラムとテストコードは以下です。
テストプログラム
結果は次の通り。
ChromeはSafari5よりも基本演算の性能が2倍以上高くなっています。しかし、getImagePixels/putImagePixelsでは、逆にSafari5の方が3倍以上高速です。Chromeはベンチマークでよく使われる基本演算に特化して最適化されており、ベンチマークの少ないCanvas系の最適化はあまり行われていない印象です。
加算と乗算については、Safari5を除いて、速度差はありません。命令のクロック数の差よりも、JavaScriptのコストの差の方が大きいため、乗算を、加算やシフトに置換してもメリットは少なそうです。
floatで演算するよりも、|0をしてintの空間にしてから計算した方が速いという話もありましたが、計測結果を見る限り、|0のコストの方が高いように思われます。
ループ条件の最適化については、AndroidとChromeの場合はfor(var i=0;i<a.length;i++)と書いてもcnt=a.length; for(var i=0;i<cnt;i++)と書いても、速度差は無いようです。しかし、iOSやSafariの場合は、依然として2倍程度の差がありますので、特にiOS向けに開発する場合は、ループ条件のローカル変数へのコピーは必須のようです。また、iOSはループ内での関数呼び出しのコストが、他よりも高いですね。
getImageDataとputImageDataには非対称性があるようです。getImageDataよりもputImageDataの方が10倍も高速です。getImageDataは意外と重いみたいなので、使いすぎに注意ですね。
とりあえずiOSにおける高速化のポイントをまとめると、以下のような感じです。
・配列の.lengthをローカル変数にコピーしてからループすると2倍高速になる
・関数はできるだけインライン展開する
・getImageDataはputImageDataの10倍遅い
・乗算をシフトや加算に置換してもあまり速くならない
・|0でintにキャストして演算すると逆に遅くなる
全体的に、iOSの処理時間は、PCで実装した処理時間の10倍程度遅いと見積もっておけばよさそうです。
テストプログラム
結果は次の通り。
. | |||||||
. | 計測項目 | iOS5(NewIpad) | Android4(Nexus) | Safari5(core i7) | Chrome19(core i7) | ||
. | 基本演算 | a++ | 386 | 118 | 48 | 2 | |
. | a+b(int) | 441 | 104 | 48 | 25 | ||
. | a+b(int)(|0) | 672 | 132 | 49 | 25 | ||
. | a+b(float) | 479 | 74 | 78 | 25 | ||
. | a*b | 521 | 103 | 93 | 31 | ||
. | a/b | 693 | 395 | 123 | 70 | ||
. | a<<b | 411 | 67 | 49 | 43 | ||
. | 配列 | array.lengthでループ | 2052 | 341 | 133 | 38 | |
. | array.lengthをローカル変数にコピーしてループ | 1111 | 353 | 89 | 38 | ||
. | ループ内で関数呼び出し | 1949 | 350 | 106 | 45 | ||
. | Canvas | getImageData | 511 | 203 | 53 | 167 | |
. | putImageData | 43 | 195 | 5 | 116 | ||
. | 単位はmsec |
ChromeはSafari5よりも基本演算の性能が2倍以上高くなっています。しかし、getImagePixels/putImagePixelsでは、逆にSafari5の方が3倍以上高速です。Chromeはベンチマークでよく使われる基本演算に特化して最適化されており、ベンチマークの少ないCanvas系の最適化はあまり行われていない印象です。
加算と乗算については、Safari5を除いて、速度差はありません。命令のクロック数の差よりも、JavaScriptのコストの差の方が大きいため、乗算を、加算やシフトに置換してもメリットは少なそうです。
floatで演算するよりも、|0をしてintの空間にしてから計算した方が速いという話もありましたが、計測結果を見る限り、|0のコストの方が高いように思われます。
ループ条件の最適化については、AndroidとChromeの場合はfor(var i=0;i<a.length;i++)と書いてもcnt=a.length; for(var i=0;i<cnt;i++)と書いても、速度差は無いようです。しかし、iOSやSafariの場合は、依然として2倍程度の差がありますので、特にiOS向けに開発する場合は、ループ条件のローカル変数へのコピーは必須のようです。また、iOSはループ内での関数呼び出しのコストが、他よりも高いですね。
getImageDataとputImageDataには非対称性があるようです。getImageDataよりもputImageDataの方が10倍も高速です。getImageDataは意外と重いみたいなので、使いすぎに注意ですね。
とりあえずiOSにおける高速化のポイントをまとめると、以下のような感じです。
・配列の.lengthをローカル変数にコピーしてからループすると2倍高速になる
・関数はできるだけインライン展開する
・getImageDataはputImageDataの10倍遅い
・乗算をシフトや加算に置換してもあまり速くならない
・|0でintにキャストして演算すると逆に遅くなる
全体的に、iOSの処理時間は、PCで実装した処理時間の10倍程度遅いと見積もっておけばよさそうです。
コメント