1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

関数を分割すると処理が遅くなる? 検証してみた!

Posted at

動機

リファクタリングの本を読んでいるとき、5行ルールというものが紹介されていました。
詳細についてはこちらの本を読んでいただきたいですが、ざっくりいうと、1つの関数にあれやこれやと詰め込むのを避けましょうということだった気がします。

そのとき、このような疑問が浮かびました。

「サブルーチンが増えるとパフォーマンスが悪化しないか・・・?」

サブルーチンの呼び出しには、当然オーバーヘッドがかかるはずです。これは無視できるほど小さいのでしょうか?

実際に計測して確かめてみようと思います。

計測

巨大な関数(サブルーチンなし)

function main() {
    let result = 0
    for(let i = 0; i <= 1000; i++) {
        result += i;
    }
    console.log(result);
}

console.time('test')
main();
console.timeEnd('test')

分割された関数

function sub() {
    let result = 0
    for(let i = 0; i <= 1000; i++) {
        result += i;
    }
    
    return result;
}

function main() {
    const result = sub();
    console.log(result);
}

console.time('test');
main();
console.timeEnd('test');

実行結果

Chrome開発者ツールのコンソールに、上記のコードを貼り付けて実行してみました。

計測結果 巨大な関数 分割された関数
1回目 0.2431640625 ms 0.281982421875 ms
2回目 0.135009765625 ms 0.283935546875 ms
3回目 0.175048828125 ms 0.326904296875 ms
4回目 0.212890625 ms 0.15283203125 ms
5回目 0.1669921875 ms 0.33984375 ms
平均 0.18662109375 ms 0.277099609375 ms

5回実行した平均値を取ってみると、分割された関数の方が、0.1msほど遅いことが確認できました。

とはいえ、2つの差は誤差程度でしかありませんね。

サブルーチンを増やしてみる

上記の差分がサブルーチンによって起きているのなら、もっとサブルーチンを呼び出せば、差は広がっていくはずです。
これを検証してみたいと思います。

巨大な関数(サブルーチンなし)

function main() {
    for(let i = 0; i <= 100; i++) {
        let result = 0
        for(let j = 0; j <= 1000; j++) {
            result += j;
        }
    }
}

console.time('test')
main();
console.timeEnd('test')

分割された関数

function sub() {
    let result = 0
    for(let j = 0; j <= 1000; j++) {
        result += j;
    }
}

function main() {
    for(let i = 0; i <= 100; i++) {
        sub();
    }
}

console.time('test')
main();
console.timeEnd('test')

実行結果

計測結果 巨大な関数 分割された関数
1回目 1.380126953125 ms 0.98486328125 ms
2回目 1.779052734375 ms 0.7509765625 ms
3回目 1.277099609375 ms 0.815185546875 ms
4回目 1.10693359375 ms 0.94091796875 ms
5回目 1.1201171875 ms 1.724853515625 ms
平均 1.332666015625 ms 1.043359375 ms

・・・サブルーチンがあるほうが早い?

理由の考察

JavaScriptのJITが最適化を行った結果、分割された関数の方が速くなったと考えられます。

今回はChrome開発者ツールのコンソールを使って計測を行ったので、JS実行環境としてV8が使われています。詳細は省きますが、V8では、同様のコードが繰り返し実行されている場合、最適化が行われることがあります。最適化されたコードは最適化されていないコードと比べると実行時間が短いです。
サブルーチンとして定義していたsub()は、短時間のうちに、全く同じ役割で何度も呼び出されています。この過程で最適化が行われ、短い時間で実行することができるようになったのだと考えられます。

まとめ

1つの関数を短くすることは、可読性の側面が大きいのかなと思っていましたが、試してみたことで、処理速度の面でも効果があるかもしれないという示唆を得ることができました。

手を動かしてみることは大事ですね!

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?