読者です 読者をやめる 読者になる 読者になる

PHPテンプレートエンジンベンチマーク パート2 Smarty vs PHPTAL

PHP

以前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%になります。さすがは枯れたエンジン、キャッシュが効いた場合の効果も考えてあるようです。で、SmartyPHPで競わせてますが、どちらのエンジンも、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、という考え方。