疲れている時にはコーラが飲みたくなってくる季節になりました。英語よりも日本語が得意な@satooshi_jpです。

前回はメソッドの呼び出し方を比較してみましたが、今回はオブジェクトの初期化について。

前フリ

先週のPublickeyの記事

を読んでいたら、関連記事に以前のfacebookの記事を見かけて読み直していたところ、PHPはスケーラビリティの問題がアレだよね、という記述があったので、ちょっと確かめてみた、というだけの内容。気になったのはこのへん。

Facebookが大規模スケーラビリティへの挑戦で学んだこと(前編)~800億枚の写真データとPHPのスケーラビリティ問題

それ以外にも、長い経験から発見したPHPの課題がある。新しく書いたコードが古いコードを遅くするということだ。例えば、PHPで書いたABCという機能にDEFという新機能を付け加えると、たとえABCからDEFを呼び出していなかったとしてもABCの動作が以前より遅くなるのだ。初期化コストなどが増加するためだ。PHPにはこうしたスケーラビリティの課題がある。

ところで、PinterestもInstagramもDjangoなのね。みんなあのアーキテクチャが好きなんだな。facebookの買収後、Instagramの今後の開発はどうなっていくんだろう。

テスト環境

  • Windows 7 64bit
  • Core i5 M520 2.40GHz
  • Mem 8GB
  • PHP 5.4.3 (cli)

テストコード

<?php
class TestA
{
protected $prop1;
public function do1() {}
}
class TestB
{
protected $prop1;
protected $prop2;
protected $prop3;
protected $prop4;
protected $prop5;
protected $prop6;
protected $prop7;
protected $prop8;
protected $prop9;
protected $prop10;
public function do1() {}
}
class TestC
{
protected $prop1;
public function do1() {}
public function do2() {}
public function do3() {}
public function do4() {}
public function do5() {}
public function do6() {}
public function do7() {}
public function do8() {}
public function do9() {}
public function do10() {}
}
class TestD
{
protected $prop1;
protected $prop2;
protected $prop3;
protected $prop4;
protected $prop5;
protected $prop6;
protected $prop7;
protected $prop8;
protected $prop9;
protected $prop10;
protected $prop11;
protected $prop12;
protected $prop13;
protected $prop14;
protected $prop15;
protected $prop16;
protected $prop17;
protected $prop18;
protected $prop19;
protected $prop20;
public function do1() {}
}
class TestE
{
protected $prop1;
protected $prop2;
protected $prop3;
protected $prop4;
protected $prop5;
protected $prop6;
protected $prop7;
protected $prop8;
protected $prop9;
protected $prop10;
protected $prop11;
protected $prop12;
protected $prop13;
protected $prop14;
protected $prop15;
protected $prop16;
protected $prop17;
protected $prop18;
protected $prop19;
protected $prop20;
protected $prop21;
protected $prop22;
protected $prop23;
protected $prop24;
protected $prop25;
protected $prop26;
protected $prop27;
protected $prop28;
protected $prop29;
protected $prop30;
protected $prop31;
protected $prop32;
protected $prop33;
protected $prop34;
protected $prop35;
protected $prop36;
protected $prop37;
protected $prop38;
protected $prop39;
protected $prop40;
public function do1() {}
}
function timer($closure)
{
$memStart = memory_get_usage();
$start = microtime(true);
$closure();
$diff = microtime(true) - $start;
$memDiff = memory_get_usage() - $memStart;
echo sprintf('time: %s sec mem: %s B' . PHP_EOL, $diff, $memDiff);
}
function performIteration($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
}
}
);
}
function performConstructTestA($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
$obj = new TestA;
}
}
);
}
function performConstructTestB($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
$obj = new TestB;
}
}
);
}
function performConstructTestC($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
$obj = new TestC;
}
}
);
}
function performConstructTestD($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
$obj = new TestD;
}
}
);
}
function performConstructTestE($iteration)
{
timer(
function () use ($iteration)
{
for ($i = 0; $i < $iteration; $i++) {
$obj = new TestE;
}
}
);
}
$iteration = 100000;
echo "iteration: $iteration", PHP_EOL;
performIteration($iteration);
performConstructTestA($iteration);
performConstructTestB($iteration);
performConstructTestC($iteration);
performConstructTestD($iteration);
performConstructTestD($iteration * 2);
performConstructTestE($iteration);

結果

test case | elapsed time (sec) | memory usage (B) :------------------------|:--------------------|------------------: performIteration | 0.00655198097229 | 96 performConstructTestA | 0.042675971984863 | 112 performConstructTestB | 0.10405302047729 | 112 performConstructTestC | 0.045380830764771 | 112 performConstructTestD | 0.043313980102539 | 112 performConstructTestE | 0.1728949546814 | 112 performConstructTestE2 | 0.33937191963196 | 112 performConstructTestF | 0.30402588844299 | 112

まとめ

PHPではオブジェクトの初期化に関して、プロパティの数が多いと生成に時間がかかるが、メソッドの数にはほとんど影響されないようだ。1リクエスト中にいくつも作られるオブジェクトではプロパティの数を少なくしておいた方がいいみたいだ。マジックプロパティへのアクセスにするとか__get()経由にすることで初期化コストを緩和できるなら、それもアリなのかもね。自分はあんまりPHPでそういうコードを書かないんだけど、どうも遅さが気になった時には試してみる価値はあるかもしれない。

1クラスに40個もプロパティを付けてみたのは、ただの遊び心。仕事中に見かけたらCtrl+AとDeleteキーを押してあげるといいと思うよ。