PHPテンプレートエンジンベンチマーク パート2 Smarty vs PHPTAL
以前PHPのテンプレートエンジンの実行速度を比較しましたが、
http://d.hatena.ne.jp/tanakahisateru/20081024/1224850023
あまりうまく計測できていなかったのと、ソース公開してなかったのと、いいかげん使ったマシンが古くなってきた感があったという3つの理由から、いまいちど、きちんとやり直したいと思います。
PHPのバージョンは5.3.3で、2.4GHz Core2のMacBookです。今どきこれぐらいなくちゃね。PHPTALも現行バージョンに上げましたよ。
HTML
参考用でスタティックHTMLです。以降のPHPはこれと同じ結果を生成します。(インデントや改行は厳密に再現されませんが、勘弁してください)
<html> <head> <title>The title value</title> </head> <body> <h1>The title value</h1> <table> <thead> <tr><th></th><th>Name</th><th>Phone</th></tr> </thead> <tbody> <tr> <td>0</td> <td>foo</td> <td>01-344-121-021</td> </tr> <tr> <td>1</td> <td>bar</td> <td>05-999-165-541</td> </tr> <tr> <td>2</td> <td>baz</td> <td>01-389-321-024</td> </tr> <tr> <td>3</td> <td>quz</td> <td>05-321-378-654</td> </tr> </tbody> </table> </body> </html>
PHP
生のPHPで、やりたいことを表します。
<?php // the Person class class Person { public $name; public $phone; function Person($name, $phone) { $this->name = $name; $this->phone = $phone; } } // let's create an array of objects for test purpose $people = array(); $people[] = new Person("foo", "01-344-121-021"); $people[] = new Person("bar", "05-999-165-541"); $people[] = new Person("baz", "01-389-321-024"); $people[] = new Person("quz", "05-321-378-654"); $title = 'The title value'; // execute the template require "my_template_file.php"; ?>
my_template_file.php
<html> <head> <title> <?php echo htmlspecialchars($title); ?> </title> </head> <body> <h1><?php echo htmlspecialchars($title); ?></h1> <?php if(!empty($people)): ?> <table> <thead> <tr><th></th><th>Name</th><th>Phone</th></tr> </thead> <tbody> <?php foreach($people as $idx=>$person): ?> <tr> <td><?php echo $idx; ?></td> <td><?php echo htmlspecialchars($person->name); ?></td> <td><?php echo htmlspecialchars($person->phone); ?></td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <p>No people exists.</p> <?php endif; ?> </body> </html>
Smarty
バージョン 2.6.7
<?php require "Smarty/libs/Smarty.class.php"; // the Person class class Person { public $name; public $phone; function Person($name, $phone) { $this->name = $name; $this->phone = $phone; } } // let's create an array of objects for test purpose $people = array(); $people[] = new Person("foo", "01-344-121-021"); $people[] = new Person("bar", "05-999-165-541"); $people[] = new Person("baz", "01-389-321-024"); $people[] = new Person("quz", "05-321-378-654"); // create a new template object $smarty = new Smarty(); // put some data into the template context $smarty->assign('title', 'The title value'); $smarty->assign('people', $people); // execute the template $smarty->display('my_template_file.tpl'); ?>
my_template_file.tpl
<html> <head> <title> {$title|escape:'html'} </title> </head> <body> <h1>{$title|escape:'html'}</h1> {if not empty($people) } <table> <thead> <tr><th></th><th>Name</th><th>Phone</th></tr> </thead> <tbody> {foreach from=$people key=idx item=person} <tr> <td>{$idx}</td> <td>{$person->name|escape:'html'}</td> <td>{$person->phone|escape:'html'}</td> </tr> {/foreach} </tbody> </table> {else} <p>No people exists.</p> {/if} </body> </html>
PHPTAL
バージョン 1.2.1
<?php require_once 'PHPTAL.php'; // the Person class class Person { public $name; public $phone; function Person($name, $phone) { $this->name = $name; $this->phone = $phone; } } // let's create an array of objects for test purpose $people = array(); $people[] = new Person("foo", "01-344-121-021"); $people[] = new Person("bar", "05-999-165-541"); $people[] = new Person("baz", "01-389-321-024"); $people[] = new Person("quz", "05-321-378-654"); // create a new template object $template = new PHPTAL('my_template_file.html'); // put some data into the template context $template->title = 'The title value'; $template->people = $people; // execute the template try { echo $template->execute(); } catch (Exception $e){ header("Content-Type:text/plain"); echo $e; } ?>
my_template_file.html
<html> <head> <title tal:content="title"> Place for the page title </title> </head> <body> <h1 tal:content="title">sample title</h1> <table tal:condition="people"> <thead> <tr><th></th><th>Name</th><th>Phone</th></tr> </thead> <tbody> <tr tal:repeat="person people"> <td tal:content="repeat/person/key">idx</td> <td tal:content="person/name">person's name</td> <td tal:content="person/phone">person's phone</td> </tr> </tbody> </table> <p tal:condition="not: people">No people exists.</p> </body> </html>
長いよね、すいません。まあ、こんな感じで、こんどはちゃんとab使ってクライアント側の無駄をなくしてやりました。以下、1000リクエストを処理するのにかかった時間です。1リクエストあたりの
時間は、1000で割って考えてください。5000ms/1000reqなら、5ms/reqです。
APCなし
シーケンシャル | 接続10本並列 | |
HTML | 314ms | 232ms |
PHP | 852ms | 554ms |
Smarty | 4491ms | 2512ms |
PHPTAL | 5122ms | 2848ms |
Smartyの所要時間を100%としたとき、PHPTALは、シーケンシャルの場合で+14%、10本接続で+13%でした。まあちょっと気になるぐらいです。
APCあり
シーケンシャル | 接続10本並列 | |
PHP | 632ms | 406ms |
Smarty | 1160ms | 682ms |
PHPTAL | 2033ms | 1146ms |
APCありだとSmartyがダントツ優秀ですね。PHPTALの所要時間が、シーケンシャルで+75%、10本接続で+68%になります。さすがは枯れたエンジン、キャッシュが効いた場合の効果も考えてあるようです。で、SmartyとPHPで競わせてますが、どちらのエンジンも、APCを使うと生のPHPにこれだけ迫れるんだということのほうが面白いんじゃないかと思います。
パフォーマンスで比べれば、PHPTALよりはSmartyのほうが高速でした。でも、あれだけ構文がんばってるのに、この程度の差で済んでいるというのは、素晴らしいことです。ソースのDOMが壊れないうえ、テキストノードにコードがない。
<tbody> {foreach from=$people key=idx item=person} <tr>
<tbody> <tr tal:repeat="person people">
Dreamweaverで開いたと考えてみてください。デザイナーがどれだけ喜ぶことか。いやもうほんと、コードの綺麗さに驚いてる時間がゼロと言っていいぐらい、すぐに仕事してもらえました。デザイン作業してる間、プログラマーはほとんど質問されません。これ、実体験。
そんなこんなで、自分にとってのプライマリーテンプレートエンジンは、やっぱりPHPTALです。僕の場合、本当の本当に実行速度が必要な場合は、デザイン作業をフリーズして、生のPHPをがっつり書くでしょうね。Smarty書くのもPHPページ書くのも、そんなに違わないので、Smartyで中途半端な速度にしかならないなら、いっそいきなりPHP、という考え方。