PHPでクラスのincludeを自動化するautoloadの速度を測ってみた。

こんな機能があるなんて知りませんでした。割と古いPHPで頭が止まっていましたが、久しぶりにPHPのドキュメントを呼んでいて、autoloadという機能があることを知りました。

大規模プロジェクトだと、クラスをincludeするだけで一苦労

超大雑把に説明すると、以下のようなソースコードはエラーを吐きます。

<?php
$obj = new MyClass();

このとおり。

PHP Fatal error:  Class 'MyClass' not found in /Users/katty/Documents/AutoLoader/index.php on line 2

そもそも、MyClassというクラスが定義されているファイルをincludeしないと使えないというエラーです。

規模が大きくなってきて、クラスが100個とか200個とかになってくると、includeするだけでも一苦労だったりしますね。

クラスを使うためにソースコードを自動でincludeしてくれる

そこで、「クラスのオートローディング」です。

spl_autoload_register() なり、__autoload() を使うことで、クラスが定義されていなかった場合に、必要なソースコードを動的にincludeすることができます。

でも遅いんでしょう?

というわけで、少しベンチマークしてみました。PHPのバージョンは5.3.10。マシンは、MacBook Air(1.7 GHz Intel Core i5)です。

bench.php

メインのファイル”bench.php”は以下のような形。時間を測定して、$argv[1]に指定した方式でクラスをincludeし、$argv[2]で指定した数だけクラスを呼び出しています。呼び出し方は2種類で、newでインスタンスを生成するパターンと、staticなメソッドを呼ぶパターンです。

<?php
 
$begin = microtime();
 
include ('./' . $argv[1] . '.php');
 
for ($i = 0; $i < $argv[2]; $i++) {
	$class_name = 'Class' . $i;
	if ($i % 2 == 0)
		$class = new $class_name();
	else
		$class_name::static_function();
}
 
echo((elapsed_time($begin, microtime()) * 1000) . "ms");
 
function elapsed_time($begin, $end) {
 
	list($usec, $sec) = explode(" ", $begin);
	$begin = ((float)$sec + (float)$usec);
 
	list($usec, $sec) = explode(" ", $end);
	$end = ((float)$sec + (float)$usec);
 
	return $end - $begin;
 
}

require.php

requireでクラスを読み込む場合の測定のための”require.php”は以下です。事前に1000個のクラスをclassesディレクトリに作成しておいて、それを全部読み込んでいます。

<?php
 
for ($i = 0; $i < 1000; $i++)
	require './classes/Class' . $i . '.php';

autoload.php

autoloadを使ってクラスを読み込む場合の測定のための”autoload.php”は以下です。クラスがなければ、ここで定義する関数に処理が渡り、クラス名を元にincludeを実行するものとしています。(今考えたら、ここもrequireにすべきでした・・・)

<?php
 
spl_autoload_register(function($class) {
	include 'classes/' . $class . '.php';
});

requireを使う場合

まず、1000クラスを読み込んだ上で、10クラスを使う場合です。

$ php bench.php require 10
Class0::__construct
Class1::static_function
# ... 略 ...
Class8::__construct
Class9::static_function
56.918859481812ms

続いて100クラス使う場合。

$ php bench.php require 100
Class0::__construct
Class1::static_function
# ... 略 ...
Class98::__construct
Class99::static_function
55.775880813599ms

1000クラス使う場合。

$ php bench.php require 1000
Class0::__construct
Class1::static_function
# ... 略 ...
Class998::__construct
Class999::static_function
65.891027450562ms

最初に1000クラスを読み込むので、使わないクラスは無駄に読み込まれたことになります。1000クラス読み込むとだいたい50msくらいで、それを使う時間は読み込む時間に比べると大きくないですね。

requireで1クラス読むのにかかる時間はだいたい0.5msというところでしょうか。

autoloadを使う場合

autoloadは必要になった場合にはじめて読み込みます。10クラス使う場合は、10個のソースコードを読み込ます。

$ php bench.php autoload 10
Class0::__construct
Class1::static_function
# ... 略 ...
Class8::__construct
Class9::static_function
1.3880729675293ms

100クラスの場合。

$ php bench.php autoload 100
Class0::__construct
Class1::static_function
# ... 略 ...
Class98::__construct
Class99::static_function
11.373043060303ms

1000クラスの場合

$ php bench.php autoload 1000
Class0::__construct
Class1::static_function
# ... 略 ...
Class998::__construct
Class999::static_function
100.74710845947ms

不要なクラスを読まないので、10クラスの場合は、990クラスを無駄にしているrequireよりも速いです。1000クラスの場合は、どちらも無駄がないので、autoloadのオーバーヘッドが見える分、autoloadの場合の方が遅いです。

autoloadの場合はだいたい1クラスで1msというところです。

まとめ

autoloadで読み込むと、requireで読み込む場合の2倍くらい遅いです。しかし、クラスの読み込みを意識せずに実装することができるので、うまく使えば素晴らしい威力を発揮してくれそうです。でもパフォーマンスがシビアな場合は無理ですね。

About katty0324

Leave a Reply

Scroll To Top