当記事では、表題の通り
「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 スーパーリロードをすると、挙動が変わる。
このあたり、良く分からない。
結論
まあ、結論としては、確かに、ネストした深いオブジェクトのデータにアクセスするのは、若干遅いようだが、気にするほどではないと言う事かな。



コメント