Edited at

PHPの【クロージャー、クラスのnew、クローン】の比較

More than 3 years have passed since last update.

【訂正】

速度の比較で、関数だけは実行時間が入っていたので、比較対象から外しました。それにともなって、タイトルを変更しました。

【/訂正】

PHPのクロージャーのおかげで、コードをデータのように使えるようになりました。ちょっとした設定とか、コードで記述できて便利です。

ところで、パフォーマンスはどうなのでしょう?

PHPの内部では、クロージャーは__invokeのあるオブジェクトとして実装されている、と聞いているので、おそらくクラスをnewするのと似たような速度かなと思います。今さらながら、遅かったらどうしよう、という疑問が…

ということで計測してみました。

比較対象は、クロージャー、クラスをnew、ついでにクローンと関数も比較してみました。


結果

     closure:   491.33  msec

new class: 437.70 msec
clone obj: 353.54 msec

実行環境:mac mini, Core i7, 16GB, PHP5.6.16


感想

予想通り、クロージャーもnewも似たような数字になりました。少しnewの方が速いのですね。クロージャーのほうが、少し複雑なオブジェクトなのかもしれません。

意外だったのが、関数呼び出しが一番遅かったこと。

関数にするより、クロージャー使ったりオブジェクトを作るほうが速いとは。と言っても、3〜4倍程度なので、そもそもが速いので、気にすることはないですが。

クローンは速いと聞いてたのですが、やはり一番早かったです。でも、似たような数字ですね。


PHP7

PHP7でも試してみたいですね。

同じく導入される無名クラスも、一応測ってみたい。


結論

コードによる速度差なんか気にしなくてOK!


コード

測定に使ったコードです。

<?php

function runIt($closure, $title)
{
$n = 1000000;
$time1 = microtime(true);
$closure($n);
$time2 = microtime(true);

echo sprintf("%12s: %8.2f msec\n", $title, ($time2 - $time1) * 1000);
};

$do_closure = function ($n = 100)
{
for($i = 0; $i < $n; $i++) {
$s = function() use($i) {return 'hi';};
}
};

class sample {
function __invoke() {
return 'hi';
}
}

$do_class = function ($n = 100)
{
for($i = 0; $i < $n; $i++) {
$s = new sample($i);
}
};

$do_clone = function ($n = 100)
{
$s0 = new sample();
for($i = 0; $i < $n; $i++) {
$s = clone $s0;
}
};

function test_func($i) {
return 'hi';
}
$do_function = function ($n = 100)
{
for($i = 0; $i < $n; $i++) {
$s = test_func($i);
}
};

//runIt($do_function, 'function');
runIt($do_closure, 'closure');
runIt($do_class, 'new class');
runIt($do_clone, 'clone obj');