LoginSignup
22
14

More than 5 years have passed since last update.

定数を返す函数はstaticでキャッシュすべきか?

Last updated at Posted at 2014-11-21

結論

  • PHPは可能な限り新しいバージョンを利用せよ
  • 静的変数静的プロパティでキャッシュするのが良い
  • メソッドと函数(クラスに属すか否か)では大差はないので、設計上望ましい方を利用すべきか

小さな配列でたかだか数回しか呼ばれないようなパターンでは気にするまでのことではない。

はじめに

「定数を返す函数」といふものが欲しいことがある。

constや外に露出した変数ではなく函数(メソッド)呼び出しにするのは、実際にメソッド呼び出しされるまで遅延できるからだ。

疑問

さて、あるとき疑問が湧いた。staticで保存しておかなくても別に良いのではないか…?

<?php

class Hoge
{
    private static $array = null;

    /**
     * よくやるやつ
     */
    function func_static()
    {
        if (!self::$array) {
            self::$array = [1, 2, 3];
        }

        return $array;
    }

    /**
     * 別にこれでいいんじゃね…?
     */
    function func_direct()
    {
        return [1, 2, 3];
    }
}

まあ、この例なら正直どっちでも良いだろう。しかし、要素数や要素の内容が巨大な配列を一度の実行で何度も参照するような場合ならどうだろうか。

といふわけで気になったので「推測するな、計測せよ」の原則に従って、仕事をさぼってベンチマークのコードを書いてみた。

ベンチマーク

ジェネレータは https://gist.github.com/zonuexe/e8df881a692f45caf488 に置いた。

比較対象は「函数のstaticでキャッシュしたもの」「函数のreturnに直接リテラルを書いたもの」「クラスの静的メンバー変数にキャッシュしたもの」「メソッドのreturnに直接リテラルを書いたもの」「メソッドのstaticでキャッシュしたもの」だ。

表記はstatic が静的変数または静的プロパティにキャッシュしたもの、 direct がキャッシュをせずに return にリテラルを直接書いたものだ。

前半の5行はベンチマーカー内で指定回数*100回の函数(メソッド)呼び出しを繰り返す。後半の5行はsystemphp -rを起動し、ワンライナー内で指定回数*3回函数(メソッド)呼び出しをする。

前半と後半の方式は測定方法が異なるので、直接比較することはできない。この試行を5回実行し、最終的にその平均値を出力する。

10x10

# 10Byteの文字列10要素の配列を取り出すベンチマーカーを生成
$ ./gen 10 10
$ php ./src/10x10.php 10
Benchmark (10 times run)
10 Bytes x 10 elements
PHP 5.5.19

average : 
        static_func:            0.000269 sec.
        direct_func:            0.001261 sec.
        static_class:           0.000377 sec.
        static_method:          0.000281 sec.
        direct_method:          0.001373 sec.
        static_other_func:      0.182772 sec.
        direct_other_func:      0.182906 sec.
        static_other_class:     0.182361 sec.
        static_other_method:    0.182405 sec.
        direct_other_method:    0.182568 sec.

staticは安定してdirectよりも早いことは注目すべきだろう。

しかしながら、プロセスを100回起動して3回だけ値を取り出すケースにおいてはそれ以外のぶれの方が大きく、誤差としてdirectの方が勝ってしまったケースがある。

よって、この程度の小さい配列を取り出したい場合においては、書きやすい方で書くのが良いだろう。

1000x10000

# 1KByteの文字列10000要素の配列を取り出すベンチマーカーを生成
$ ./gen 1000 10000
$ php ./src/1000x10000.php
Benchmark (1 times run)
1000 Bytes x 10000 elements
PHP 5.5.19

average : 
        static_func:            0.000390 sec.
        direct_func:            0.470571 sec.
        static_class:           0.000657 sec.
        static_method:          0.000380 sec.
        direct_method:          0.480114 sec.
        static_other_func:      0.508610 sec.
        direct_other_func:      0.519826 sec.
        static_other_class:     0.511884 sec.
        static_other_method:    0.509537 sec.
        direct_other_method:    0.522021 sec.

この例において、100回呼び出す際にはdirectは圧倒的に遅いので 絶対利用すべきではない 。また静的プロパティよりも静的変数の方が早いので、同じクラスの別メソッドと共有する必要がない場合は静的変数にしておくと、気持ち早くなることは期待できる。

大差はないが、全体的にstaticの方が若干早い。数回しか呼び出されないとしても静的変数にしておくのが無難か。

最後に

今回の実験でPHP 5.5.10と5.5.19を利用したが、筆者の環境では5.5.19の方が有意に早かったので、基本的にはマイナーバージョンの差であっても最新の環境を利用するのが望ましい。

22
14
3

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
22
14