結論
小さな配列でたかだか数回しか呼ばれないようなパターンでは気にするまでのことではない。
はじめに
「定数を返す函数」といふものが欲しいことがある。
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行はsystemでphp -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の方が有意に早かったので、基本的にはマイナーバージョンの差であっても最新の環境を利用するのが望ましい。