2048*2048[px]の画像に対してJavaScriptでフィルタをかけることを想定して、2048*2048*4回の基本演算の所要時間と、2048*2048[px]のCanvasに対するgetImagePixelsとputImagePixelsの速度を計測しました。テストプログラムとテストコードは以下です。

テストプログラム

結果は次の通り。

.

.

計測項目iOS5(NewIpad)Android4(Nexus)Safari5(core i7)Chrome19(core i7)

.

基本演算a++386118482

.

a+b(int)4411044825

.

a+b(int)(|0)6721324925

.

a+b(float)479747825

.

a*b5211039331

.

a/b69339512370

.

a<<b411674943

.

配列array.lengthでループ205234113338

.

array.lengthをローカル変数にコピーしてループ11113538938

.

ループ内で関数呼び出し194935010645

.

CanvasgetImageData51120353167

.

putImageData431955116

.

単位は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倍程度遅いと見積もっておけばよさそうです。