「Smarty は速い」は都市伝説

Smarty が広まった理由のひとつに、動作が速いというのがある。Smarty はテンプレートをコンパイルして PHP ファイルに変換し、それを実行する。そのため、実行速度が速いと信じられている。

しかし、これは大きな誤解である。PHP ファイルを include() する方法と比べ、Smarty は約 2〜3 倍遅い。なぜなら、Smarty のテンプレートをコンパイルしてできる PHP ファイルがクソだから。つまり、いくら PHP ファイルに変換するからといっても、遅いコードに変換されるんだから、人間が書いたふつうの PHP ファイルのほうがよっぽど速いというわけ。

たとえば次のようなコードがあるとする。

<table>
{section loop=$list name=item}
  <tr bgcolor="{cycle values='#FFCCCC,#CCCCFF'}">
    <td>{$list[item]|escape}</td>
  </tr>
{/section}
</table>

これをコンパイルすると、こんなクソみたいな PHP コードが生成される。

<?php /* Smarty version 2.6.18, created on 2008-01-01 00:00:00
         compiled from template3.tpl */ ?>
<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
smarty_core_load_plugins(array('plugins' => array(array('modifier', 'escape', 'template3.tpl', 1, false),array('function', 'cycle', 'template3.tpl', 4, false),)), $this); ?>
<h1><?php echo ((is_array($_tmp=$this->_tpl_vars['title'])) ? $this->_run_mod_handler('escape', true, $_tmp) : smarty_modifier_escape($_tmp)); ?>
</h1>
<table>
<?php unset($this->_sections['item']);
$this->_sections['item']['loop'] = is_array($_loop=$this->_tpl_vars['list']) ? count($_loop) : max(0, (int)$_loop); unset($_loop);
$this->_sections['item']['name'] = 'item';
$this->_sections['item']['show'] = true;
$this->_sections['item']['max'] = $this->_sections['item']['loop'];
$this->_sections['item']['step'] = 1;
$this->_sections['item']['start'] = $this->_sections['item']['step'] > 0 ? 0 : $this->_sections['item']['loop']-1;
if ($this->_sections['item']['show']) {
    $this->_sections['item']['total'] = $this->_sections['item']['loop'];
    if ($this->_sections['item']['total'] == 0)
        $this->_sections['item']['show'] = false;
} else
    $this->_sections['item']['total'] = 0;
if ($this->_sections['item']['show']):

            for ($this->_sections['item']['index'] = $this->_sections['item']['start'], $this->_sections['item']['iteration'] = 1;
                 $this->_sections['item']['iteration'] <= $this->_sections['item']['total'];
                 $this->_sections['item']['index'] += $this->_sections['item']['step'], $this->_sections['item']['iteration']++):
$this->_sections['item']['rownum'] = $this->_sections['item']['iteration'];
$this->_sections['item']['index_prev'] = $this->_sections['item']['index'] - $this->_sections['item']['step'];
$this->_sections['item']['index_next'] = $this->_sections['item']['index'] + $this->_sections['item']['step'];
$this->_sections['item']['first']      = ($this->_sections['item']['iteration'] == 1);
$this->_sections['item']['last']       = ($this->_sections['item']['iteration'] == $this->_sections['item']['total']);
?>
  <tr bgcolor="<?php echo smarty_function_cycle(array('values' => '#FFCCCC,#CCCCFF'), $this);?>
">
    <td><?php echo ((is_array($_tmp=$this->_tpl_vars['list'][$this->_sections['item']['index']])) ? $this->_run_mod_handler('escape', true, $_tmp) : smarty_modifier_escape($_tmp)); ?>
</td>
  </tr>
<?php endfor; endif; ?>
</table>


ぱっと見た限りでは、次のようなことがわかる。

  • テンプレートにおける変数への参照が、コンパイル後の PHP コードではネストした配列の要素へのアクセスとなっている。
  • ループするごとに、使ってもいない各種ループカウンタを設定している。
  • escape のような modifier を呼び出すときは、いちいち is_array() で配列かどうか調べている。


こんなことしてたら、いくら PHP コードにコンパイルしても遅いに決まっている。どう考えても、普通の PHP ファイルを include() するほうが速いし、なにより簡単。

このように、「Smarty が速い」なんていうのは誤った認識である。スピードを理由に Smarty を選ぶのはやめたほうがよい。


以上、Smarty 撲滅委員会からのお知らせでした。