JavaScript の高速化その2 「全てを疑い、自分の目で確認すること」
こういう泥臭い資料作りもやってるので、一応書き残します。
問題1. array.length へのアクセス
var ary = new Array(100000); な配列があるとします。
IE6環境下で、配列の長さを求める方法を、早い順に並べてください。
TEST1. a.length;
TEST2. var L = "length"; a[L];
TEST3. a["length]";
正解は、TEST1 < TEST3 < TEST2 です。
Browser | TEST1 | TEST2 | TEST3 | 総評 |
Chrome | 1 | 1 | 1 | 全て同じスコア |
Safari | 1 | 2 | 3 | TEST2はTEST1の2倍, TEST3に至っては3.4倍スコアが違う |
Opera9.27 | 1 | 1 | 1 | 全て同じスコア |
Opera9.6β | 1 | 2 | 2 | 1.3倍スコアが違う |
Firefox2 | 1 | 2 | 1 | TEST1とTEST2で1.5倍違う |
Firefox3 | 2 | 1 | 2 | TEST2が12%早い |
IE6 | 1 | 3 | 2 | |
IE8β2 | 1 | 2 | 1 | TEST1とTEST2で1.2倍違う |
数値は順位
配列の要素数が欲しければ、 a.length を使いましょう。
問題2. document.getElementById() へのアクセス
<div id="root"></div>
IE6環境下で、この要素を検索する方法を、早い順に並べてください。
TEST10. document.getElementById("root");
TEST11. var ID = "getElementById"; document[ID]("root");
TEST12. var _doc = document; _doc.getElementById("root");
TEST13. var _doc = document, ID = "getElementById"; _doc[ID]("root");
正解は、TEST12 < TEST13 < TEST10 < TEST11 です。TEST12とTEST13の差は0.0058%0.58%です。
TEST12とTEST11では5.6%の差が見られます。
Browser | TEST10 | TEST11 | TEST12 | TEST13 | 総評 |
Chrome | 3 | 3 | 1 | 2 | 微妙にTEST12が早い |
Safari | 2 | 4 | 1 | 3 | TEST10とTEST11では2倍スコアが違う |
Opera9.27 | 3 | 4 | 1 | 1 | TEST12とTEST11では2倍スコアが違う |
Opera9.6β | 3 | 4 | 1 | 2 | TEST12とTEST11では1.8倍スコアが違う |
Firefox2 | 3 | 4 | 1 | 2 | TEST11とTEST12では1.3倍スコアが違う |
Firefox3 | 2 | 4 | 1 | 3 | TEST11とTEST12では2.1倍スコアが違う |
IE6 | 3 | 4 | 1 | 2 | |
IE8β2 | 4 | 4 | 1 | 2 | TEST11とTEST12では1.6倍スコアが違う |
数値は順位
idで要素を検索したければ、var _doc = document; _doc.getElementById("root"); を使いましょう。
問題3. ネストされたプロパティへのアクセス
これと function hoge() { var ary = Array(10000); return Array.prototype.slice.call(ary); } これでは? function hoge() { var ary = Array(10000), slice = Array.prototype.slice; return slice.call(ary); }
IE6では後者が7%ほど早くなります。Chromeでも1%ほど高速化されます。
結論
lengthプロパティにアクセスする場合は、小細工はしないほうが良く、
ネストされたプロパティにアクセスする場合は、ネストを浅くするためにキャッシュしておいたほうが良く、
documentにアクセスする場合には、documentをローカル変数にキャッシュしたほうが速度が向上します(ローカルスコープの中にですよ)。
つまり
var _doc = document; // これだと、いまいち効果が薄いので、 function hoge() { var e = _doc.getElementById("piyo"); } function hoge() { var _doc = document; // 面倒でもローカルスコープ内で宣言する必要がある。 var e = _doc.getElementById("piyo"); }
プロパティによっては特別扱いされているものもあるかもしれませんが、原則こんな感じでいけます。
ほかには、
var i = 0, sz = elm.childNodes.length; for (; i < sz; ++i) { elm.childNodes[i] }
よりも
var c = elm.firstChild; for (; c; c = c.nextSibling) { c }
が、60倍以上高速だったりします(in IE6)。
あとは、Papervision3D を JavaScriptに移植しようとたくらみ、コードリードしていた時に、こんな感じの(関数の引数をわざわざローカル変数にマップしている)コードを沢山見かけました。
function(aa, bb, cc) { var a = aa, b = bb, c = cc; return a * b * c; }
高速化目的なのかな? と思い同様のコードをJavaScriptで試してみたら、いずれのブラウザでも期待を裏切る結果になりました。
もしかして、上記の書き方をすると、ActionScript では高速化するのかもしれません。
興味がある方はいろいろと計測してみると良いでしょう。
# 良かったら結果も教えてください。
id:amachang が以前 http://d.hatena.ne.jp/amachang/20071010/1192012056 でIE + document について言及していましたが、他のブラウザでも通用する手法のようです。