PHPテンプレートベンチマーク ---- PHPTALに注目

PHPTALのチュートリアル
http://phptal.org/manual/en/split/firstexample.html
を、

  • プレーンなHTML(出力結果のダンプ)
  • ネイティブPHP 5.2
  • Smarty 2.6.20
  • PHPTAL 1.1.3

のそれぞれで書いてみて、ベンチマークを取ってみました。(ただし、テーブルに流し込むデータが空ならTABLEをPタグとテキストに置き換え、テーブルの行には行番号を追加した)

PHPTALのXHTML出力は、文字列中のタグ文字を勝手にエスケープするので、SmartyとネイティブPHPのコードにはそれぞれ、escape:'html'とhtmlspecialcharsをつけています。でないと公平じゃないし。

ベンチマークといっても、単純に100回連続で同じリクエストを送信した結果を、特徴の違う2つのマシンで計測してみただけですが。

Windows Vista / Core2 Duo E8300 2.83GHz / Apache2.2.9 / PHP5.2.6

Processing HTML
100 times : total time 2.0660 sec (48.403 query/sec 20.66 msec/query)

Processing PHP(native)
100 times : total time 2.0900 sec (47.847 query/sec 20.90 msec/query)

Processing Smarty
100 times : total time 2.7180 sec (36.792 query/sec 27.18 msec/query)

Processing PHPTAL
100 times : total time 2.9620 sec (33.761 query/sec 29.62 msec/query)

Ubuntu-8.04 Serever / Celeron 2.40GHz / Apache2.2.8 / PHP5.2.4

Processing HTML
100 times : total time 1.0484 sec (95.383 query/sec 10.48 msec/query)

Processing PHP(native)
100 times : total time 0.9738 sec (102.693 query/sec 9.74 msec/query)

Processing Smarty
100 times : total time 2.3589 sec (42.393 query/sec 23.59 msec/query)

Processing PHPTAL
100 times : total time 2.5022 sec (39.964 query/sec 25.02 msec/query)

PHPTALとSmartyの計測結果は誤差程度です。PHPTALのほうがやや早い場合もありました。

Smartyが遅くて使い物にならないと思い込んでいたけど、最近はそうでもないんですね。遅いと思い込んでいたのは何年前の話やら…。PHP5がまだマイナーで、普通のCPUがPentium3だったころの印象から、完全にPHPテンプレートエンジンを避けていました。しまった、 http://d.hatena.ne.jp/tanakahisateru/20080924/1222190229Smartyをけなしすぎました。Smartyごめんよ。

CPUに瞬発力があるなら、場合によってはSmartyを使ってもいいかもしれないけど、SmartyはやっぱりPHPタグの別の書き方でしかない(PHPタグと同じ場所に別の構文で同じ意味のことを書く)ので、やっぱり速度面のデメリットがあるんなら、まだPHPそのものをテンプレート言語にするのが有利にはたらく場合のほうが多い気がしました。

ただし、書き換えに対して表示が圧倒的に多いサイトの場合、データベース接続のオーバーヘッドを避けるため、テンプレートエンジンのページキャッシュ機能を利用するのは有効かもしれません。まあ、それとて自力で制御するという手がないわけではないけど…。

それより、PHPTALとSmartyとの性能差がほとんどないということが驚きでした。「PHPとそれほど違わないのに少し重いSmarty」よりも、「同じように少し重くなるだけで、作りに画期的な変化をもたらしてくれるPHPTAL」は、注目に値しますね。

PHPTALのなにが画期的?

PHPTALには

  • タグ構造を維持したまま、属性に制御コードを書く(デザイナフレンドリ)
  • 元ページのDOMノードをラップするマクロを定義できる

という嬉しい特徴があります。

デザイナがHTMLだけ書き、プログラマがそこにコードを注入した場合、PHPSmartyだとHTMLとしての見た目が変わってしまうけど、拡張属性にコードを書くテンプレートなら、コード注入後でも見た目が変わりません。つまり、Tapestry/Kid/Genshiと同じやり方でいけるということ。

「DOMノードをラップするマクロ」ってなんのこと?ってのは要するに、

<?php include "layout/block_begin.inc"; ?>
  <p>hoge</p>
<?php include "layout/block_end.inc"; ?>

みたいなのを

<div metal:use-macro="layout.html/block">
  <p metal:fill-slot="contents">hoge</p>
</div>

と書けるということ。2つの.incファイル(<div>とそれに対応する</div>が別々になってしまう)が、単一のマクロファイルに収まります(ひとつの外部ファイルには、複数のマクロを置いとける)。Kid/Genshiのpy:matchには及ばないけど、Dreamweaverのテンプレートと編集可能領域よりは強力。

参考

100回リクエストベンチに使ったRubyスクリプト

require 'net/http'

TIMES = 100
def execprofile(name)
    print "Processing #{name}\n"
    start = Time::now
    for i in 1 .. TIMES {|| yield }
    total = Time::now - start
    printf("%d times : total time %.4f sec (%.3f query/sec %.2f msec/query)\n\n",
        TIMES,
        total.to_f,
        TIMES / total.to_f,
        1000.0 * total.to_f / TIMES
    )
end

HOST       = 'localhost'
BASEURI    = '/path/to/your/test/'  #テスト対象のURI
execprofile('HTML')       {|| Net::HTTP.get(HOST, BASEURI+'page_html.htm') }
execprofile('PHP(native)'){|| Net::HTTP.get(HOST, BASEURI+'page_php.php') }
execprofile('Smarty')     {|| Net::HTTP.get(HOST, BASEURI+'page_smarty.php') }
execprofile('PHPTAL')     {|| Net::HTTP.get(HOST, BASEURI+'page_tal.php') }