****明確化**: 最速のコードや最適化を探しているわけではありません。最適化または最適化されていないように見える一部のコードが、実際には一般的に一貫して高速に実行される理由を理解したいと思います。
ショートバージョン
このコードの理由:
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
これより高性能?
var index = Math.floor(ref_index) * 4;
ロングバージョン
今週、Impact js の作成者がレンダリングの問題に関する記事を公開しました。
http://www.phoboslab.org/log/2012/09/drawing-pixels-is-hard
この記事には、キャンバス内のピクセルにアクセスして画像をスケーリングする関数のソースがありました。この種のコードを最適化する従来の方法をいくつか提案して、読み込み時のスケーリングを短くしたいと思いました。しかし、それをテストした後、私の結果はほとんどの場合、元の機能よりも最悪でした.
これは、スマートな最適化を行っている JavaScript エンジンであると推測し、何が起こっているのかをもう少し理解しようとしたので、たくさんのテストを行いました。しかし、私の結果は非常に紛らわしく、何が起こっているのかを理解するには助けが必要です.
ここにテストページがあります:
http://www.mx981.com/stuff/resize_bench/test.html
jsPerf: http://jsperf.com/local-variable-due-to-the-scope-lookup
テストを開始するには、画像をクリックします。結果がコンソールに表示されます。
次の 3 つの異なるバージョンがあります。
元のコード:
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
var indexScaled = (y * widthScaled + x) * 4;
scaledPixels.data[ indexScaled ] = origPixels.data[ index ];
scaledPixels.data[ indexScaled+1 ] = origPixels.data[ index+1 ];
scaledPixels.data[ indexScaled+2 ] = origPixels.data[ index+2 ];
scaledPixels.data[ indexScaled+3 ] = origPixels.data[ index+3 ];
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
それを最適化する私の試みの1つ:
var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = Math.floor(ref_index) * 4;
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];
ref_index+= ref_step;
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
同じ最適化コードですが、毎回インデックス変数を再計算します (ハイブリッド)
var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];
ref_index+= ref_step;
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
最後の 2 つの唯一の違いは、'index' 変数の計算です。そして驚いたことに、最適化されたバージョンはほとんどのブラウザー (Opera を除く) で遅くなります。
個人テストの結果(jsPerf テストではありません):
オペラ
Original: 8668ms Optimized: 932ms Hybrid: 8696ms
クロム
Original: 139ms Optimized: 145ms Hybrid: 136ms
サファリ
Original: 433ms Optimized: 853ms Hybrid: 451ms
ファイアフォックス
Original: 343ms Optimized: 422ms Hybrid: 350ms
掘り下げた後、スコープルックアップのために主にローカル変数にアクセスするのが通常の良い習慣のようです。最適化されたバージョンは 1 つのローカル変数のみを呼び出すため、関連するさまざまな操作に加えて、複数の変数とオブジェクトを呼び出すハイブリッド コードよりも高速になるはずです。
では、なぜ「最適化された」バージョンが遅いのでしょうか?
一部の JavaScript エンジンが Optimized バージョンを最適化していないことが原因ではないかと考えていましたが--trace-opt
、chrome で使用したところ、すべてのバージョンが V8 で適切にコンパイルされているようです。
この時点で、私は少し無知で、誰かが何が起こっているのか知っているのだろうか?
このページでさらにいくつかのテストケースも行いました。