当記事では、表題の通り
「JavaScript オブジェクトのネストした深い要素にアクセスすると遅い」
件について調査した。
では、本題に入ろう。
JavaScript オブジェクトのネストした深い要素にアクセスすると遅い
JavaScriptの場合、
var a = 1; // 整数 var d = 1.0; // 実数 var str1 = '文字列1' ; var str2 = "文字列2" ; var ary = [1, 2.0, '文字列' ]; // 整数、実数、文字列を要素に持つ配列 var obj ={int: 1, dbl: 2.0, str: "文字列"}; // 整数、実数、文字列を要素に持つオブジェクト
など、いろんなデータ形式がある。
配列やオブジェクトの場合には、ネスト(入れ子に)する事も可能だ。
例えば、
var obj1 ={ str: '文字列'}; var obj2 ={ obj1: obj1};
としてやれば、
obj2.obj1.str
は、
'文字列'
になる。
いくらでもネスト出来る。
こういうデータ形式は、WEBプログラミングをしていると良く出て来る。
例えば、
window.location.href
だ。
今開いているサイトのurlが得られる。
で、今日、ふと疑問に思った。
window.location.href
と言うデータを何回も使うよりも
var url = window.location.href;
と代入しておいて url を使い回すほうが少しは速いんじゃないだろうかと。
つまり、毎回、深いネストの末尾までデータを探しに行くのは効率が悪いのでは無いかと。
気になったので計測してみた。
即席で計測プログラムを作った。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <button onclick="button_onclick_CB()">計測開始</button> <textarea id="textareaID" style="width:500px;height:200px;"></textarea> <script type="text/javascript"> "use strict"; function get_datetime_millisec() { if (typeof window.performance !== 'undefined' && typeof window.performance.now !== 'undefined') { //(1) window.performance.now() // プログラム開始直後からのミリ秒。高精度 // ie9で使えないようだ。Safariでも未サポート?要確認 var datetime1 = window.performance.now(); return datetime1; } else { //(2) Date.now() // https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/now // var timeInMs = Date.now(); // UTC(協定世界時)での 1970 年 1 月 1 日 00 時 00 分 00 秒 から現在までの経過ミリ秒を返します。 var datetime2 = Date.now(); return datetime2; } } function button_onclick_CB() { var a1 = { a0: 1 }; var a2 = { a1: a1 }; var a3 = { a2: a2 }; var a4 = { a3: a3 }; var a5 = { a4: a4 }; var a6 = { a5: a5 }; var a7 = { a6: a6 }; var a8 = { a7: a7 }; var a9 = { a8: a8 }; var a10 = { a9: a9 }; var a11 = { a10: a10 }; var a12 = { a11: a11 }; var a13 = { a12: a12 }; var a14 = { a13: a13 }; var a15 = { a14: a14 }; var a16 = { a15: a15 }; var a17 = { a16: a16 }; var a18 = { a17: a17 }; var a19 = { a18: a18 }; var a20 = { a19: a19 }; var a21 = { a20: a20 }; var a22 = { a21: a21 }; var a = a22.a21.a20.a19.a18.a17.a16.a15.a14.a13.a12.a11.a10.a9.a8.a7.a6.a5.a4.a3.a2.a1.a0; var I_END = 100000000; var datetime_sta; var datetime_end; var sec_diff; var msg; var sum; //-------------------- datetime_sta = get_datetime_millisec(); sum = 0; for (var i = 0; i < I_END; i++) { sum += a; } datetime_end = get_datetime_millisec(); sec_diff = (datetime_end - datetime_sta) / 1000; msg = 'sum =' + sum + ' 計算時間 ' + sec_diff + ' [sec]'; document.getElementById('textareaID').value = msg; //------------------- datetime_sta = get_datetime_millisec(); sum = 0; for (var i = 0; i < I_END; i++) { sum += a22.a21.a20.a19.a18.a17.a16.a15.a14.a13.a12.a11.a10.a9.a8.a7.a6.a5.a4.a3.a2.a1.a0; } datetime_end = get_datetime_millisec(); sec_diff = (datetime_end - datetime_sta) / 1000; msg = 'sum =' + sum + ' 計算時間 ' + sec_diff + ' [sec]'; document.getElementById('textareaID').value += '\r\n' + msg; //------------------- } </script> </body> </html>
上のJavaScriptコードの説明
まあ、見れば分ると思うが、念のために解説しておく。
function button_onclick_CB()内ではforループが二カ所にある。
一つ目は、ループの外でaを取得しておいて、そのaをループ内で何度も足し算する。
var a = a22.a21.a20.a19.a18.a17.a16.a15.a14.a13.a12.a11.a10.a9.a8.a7.a6.a5.a4.a3.a2.a1.a0; for (var i = 0; i < I_END; i++) { sum += a; }
二つ目のループでは、足す数字をループ内で毎回参照している。
その変数は22回もネストしている。
for (var i = 0; i < I_END; i++) { sum += a22.a21.a20.a19.a18.a17.a16.a15.a14.a13.a12.a11.a10.a9.a8.a7.a6.a5.a4.a3.a2.a1.a0; }
予想としては、こちらのループは計算効率が悪いので時間が掛かるはずだ。
実行してみた。
IE11 (11.0.9600.18097)
sum =1000000 計算時間 0.03592245372911498 [sec] sum =1000000 計算時間 0.7058300217357893 [sec]
Firefox 42.0
sum =100000000 計算時間 0.0575850000000064 [sec] sum =100000000 計算時間 0.8830599999999976 [sec]
Chrome 47.0.2526.106 m (64-bit)
sum =100000000 計算時間 0.10190999999999985 [sec] sum =100000000 計算時間 4.939385 [sec]
実験結果から判断して、確かに後者のこんな計算
sum += a22.a21.a20.a19.a18.a17.a16.a15.a14.a13.a12.a11.a10.a9.a8.a7.a6.a5.a4.a3.a2.a1.a0;
をしている方が遅いようだ。
なお IE11 は Firefox, Chrome に比べて100倍くらい遅いようなので、ループ回数定数 I_END を100分の1にした。
また、キャッシュが関係しているようで、ネスト有り無しのどちらの計算時間もほぼ同じになる場合もある。
なので、CTRL+F5 スーパーリロードをすると、挙動が変わる。
このあたり、良く分からない。
結論
まあ、結論としては、確かに、ネストした深いオブジェクトのデータにアクセスするのは、若干遅いようだが、気にするほどではないと言う事かな。
コメント