【ワレコの講座】JavaScript オブジェクトのネストした深い要素にアクセスすると遅い

スポンサーリンク

当記事では、表題の通り

「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 スーパーリロードをすると、挙動が変わる。

このあたり、良く分からない。

結論

まあ、結論としては、確かに、ネストした深いオブジェクトのデータにアクセスするのは、若干遅いようだが、気にするほどではないと言う事かな。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

JavaScript
スポンサーリンク
warekoをフォローする
スポンサーリンク
われこ われこ

コメント