がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

include系の速度について

ちぃと興味があったので。


まずは以下のファイルを用意。includeされるクラスファイル、のイメージ。

<?php

//
class dummy {

public function __construct()
{
  $this->val_ = '';
  $this->val2_ = '';
  $this->val3_ = '';
  $this->val4_ = '';
}

private $val_;
private $val2_;
private $val3_;
private $val4_;

}


基本的なrequire_onceで、まずは基準になる時間を測定。

<?php

$time = microtime(true);

for($i = 0; $i < 10000; $i ++) {
  require_once('./dummy.php');
  $obj = new dummy();
}

var_dump( microtime(true) - $time);

float(0.28320693969727)
float(0.21706700325012)
float(0.21338105201721)
float(0.14879083633423)
float(0.1160900592804)
float(0.35782194137573)
float(0.10269999504089)
float(0.078638076782227)
float(0.082237958908081)
float(0.092903852462769)
平均:0.1692837715148927
大体169ミリ秒。


次。「相対パスいやぽん」って話を聞くので、軽く。

<?php

$time = microtime(true);

set_include_path(get_include_path() . PATH_SEPARATOR . '/home/furu');

for($i = 0; $i < 10000; $i++) {
  require_once('dummy.php');
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.15573406219482)
float(0.30610704421997)
float(0.30870699882507)
float(0.3240659236908)
float(0.18956899642944)
float(0.10903787612915)
float(0.05492091178894)
float(0.13507890701294)
float(0.23499989509583)
float(0.18207597732544)
平均:0.20002965927124
大体200ミリ秒。
確かに「微妙に」遅い。


次。autoloadを使ってみる。…しまったおいちゃん初めて使うよw
とりあえず極めてシンプルに実装してみるかねぇ。

<?php

$time = microtime(true);

function __autoload($classname) {
    $filename = "./" . $classname . ".php";
    require($filename);
}

for($i = 0; $i < 10000; $i++) {
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.085892915725708)
float(0.083467960357666)
float(0.08056902885437)
float(0.05281400680542)
float(0.12568616867065)
float(0.096516132354736)
float(0.10803079605103)
float(0.11510992050171)
float(0.12555193901062)
float(0.097607851028442)
平均:0.0971246719360352秒
大体97ミリ秒。なるほど速い。


一応、include pathによる方法も実験。

<?php

$time = microtime(true);

function __autoload($classname) {
    $filename = $classname . ".php";
    require($filename);
}

set_include_path(get_include_path() . PATH_SEPARATOR . '/home/furu');

for($i = 0; $i < 10000; $i++) {
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.095851898193359)
float(0.10157299041748)
float(0.086189985275269)
float(0.10135197639465)
float(0.099920988082886)
float(0.11156392097473)
float(0.078891038894653)
float(0.079911947250366)
float(0.12963700294495)
float(0.1493399143219)
平均:0.1034231662750243
大体103ミリ秒。
確かに重くなる。


次。
http://php.net/manual/ja/language.oop5.autoload.php

spl_autoload_register() を使えば、より柔軟にクラスのオートロードができます。 そのため、今や __autoload() を使うことはおすすめできません。 将来のバージョンではこの機能が非推奨となり、削除されるかもしれません。

「より柔軟にクラスのオートロードができます」って発言がすげぇ怖いんだけど、とはいえ「 そのため、今や __autoload() を使うことはおすすめできません。 将来のバージョンではこの機能が非推奨となり、削除されるかもしれません」って記述もあるので、使ってみる。
…やること一緒だから変わらない気もするが。

<?php

$time = microtime(true);

spl_autoload_register(function ($class) {
    require('./' . $class . '.php');
});

//set_include_path(get_include_path() . PATH_SEPARATOR . '/home/furu');

for($i = 0; $i < 10000; $i++) {
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.11501908302307)
float(0.069617033004761)
float(0.098038911819458)
float(0.08371901512146)
float(0.089720010757446)
float(0.068127870559692)
float(0.099350214004517)
float(0.031562089920044)
float(0.11656093597412)
float(0.04613208770752)
平均:0.0817847251892088
大体82ミリ秒。
…速い? 誤差?


include_pathも。

<?php

$time = microtime(true);

spl_autoload_register(function ($class) {
    require($class . '.php');
});

set_include_path(get_include_path() . PATH_SEPARATOR . '/home/furu');

for($i = 0; $i < 10000; $i++) {
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.098303079605103)
float(0.093553066253662)
float(0.097643136978149)
float(0.035649061203003)
float(0.091119050979614)
float(0.073930025100708)
float(0.098550081253052)
float(0.086983919143677)
float(0.087239027023315)
float(0.095355987548828)
平均:0.0858326435089111
大体86ミリ秒。…あら性能いい。


お次。お手製で「クラス存在」を確認してみる。

<?php

$time = microtime(true);

function gallu_require_once($class) {
    if (false === class_exists($class)) {
      require('./' . $class . '.php');
    }
}

for($i = 0; $i < 10000; $i++) {
  gallu_require_once('dummy');
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.16889500617981)
float(0.12661600112915)
float(0.15334701538086)
float(0.20668292045593)
float(0.070807933807373)
float(0.3076388835907)
float(0.27700400352478)
float(0.19814801216125)
float(0.060111999511719)
float(0.064663887023926)
平均:0.1633915662765498
大体163ミリ秒。まぁそんなもんだろうねぇ。


一応念のため「一番速いはず」のを一つ。

<?php

$time = microtime(true);

require('./dummy.php');

for($i = 0; $i < 10000; $i++) {
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(0.039539813995361)
float(0.036581993103027)
float(0.11284589767456)
float(0.044780969619751)
float(0.04578709602356)
float(0.049165010452271)
float(0.11754703521729)
float(0.093722105026245)
float(0.040516138076782)
float(0.083725929260254)
平均:0.0664211988449101
大体66ミリ秒。そうだねぇ予想どおり。


一個興味あるパターン。

<?php

if (false === class_exists('dummy')) {
//
class dummy {

public function __construct()
{
  $this->val_ = '';
  $this->val2_ = '';
  $this->val3_ = '';
  $this->val4_ = '';
}

private $val_;
private $val2_;
private $val3_;
private $val4_;

}

}

C言語やってた人的には「#ifdef」って書くと伝わると思う。
さてはて、どうなるだろ?

<?php

$time = microtime(true);

for($i = 0; $i < 10000; $i++) {
  require('./dummy.php');
  $obj = new dummy();
}

var_dump(microtime(true) - $time);

float(1.8714621067047)
float(1.6092789173126)
float(1.9900488853455)
float(4.8480188846588)
float(4.7369208335876)
float(2.272677898407)
float(2.5049839019775)
float(1.9312250614166)
float(2.2353899478912)
float(2.4838180541992)
平均省略w
へぇ予想してないではなかったけど、ここまでとは。


まとめ。

方法 1万回分(ミリ秒)
require 66
spl_autoload_register 82
spl_autoload_register+set_path 86
autoload 97
autoload+set_path 103
require_once 169
require_once+set_path 200


まぁある程度予想と風評の通り。
autoloadとかspl_autoload_registerとかは「newしてエラーが出た時に」フックする一方で、require_onceは「存在チェックを毎回やっている」と予想されるので、その辺の差異じゃないかなぁ、と。
とはいえ一方で、まぁ簡単に割り算もできないんだろうけど「1万回で100ミリ秒程度の差異」なので、普通プログラム的にinclude系って…どうだろうおいちゃん感覚だと100いくかどうかだと思うんだけど、100回で「1ミリ秒」程度の差異しかでないような感じでもあるので。
果たしてそこまで「目くじらを立てるほどのものなのか?」ってのも、あるっちゃぁ、ある。


とりあえず当面としては「おいちゃんはrequire_onceでいいかなぁ」程度。ただ、特にフレームワーク側はその辺「意識するにこしたことはない」ので、引き続きウォッチかなぁ、っと。


以上、実験結果の報告でした。