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

Life, Education, Death

プログラミング以外でも思ったことをつらつらと書きたい

memcachedとSmarty

はじめに

Smartyは遅い。そこでSimplateを使おうと話になった。
SimplateSmarty互換でシンプルなPHP拡張実装。実装がCなのでとても早いという評判なので早速使ってみた。

Simplateを試す

公式のインストールの項目の通り、ソースからインストールした。このときにエスケープ処理が欲しかったので
自動でエスケープするパッチを当てた。
バージョンが0.41だったが問題なく動作した。

使えるプロパティとメソッドの一覧はとても便利。


下記のコードとテンプレートを実行してSmartyと比べてどの程度の性能が出るか測ってみた。
実行環境はVMWareServer上のCentosにphp5.2.5を入れたものを使用した。GETで実行回数を渡して数千回くらい実行してみた。


実験に使用したコード

$start = microtime(true);
for( $i=0; $i<$loopCount; ++$i ){
	$simplate = new Simplate();
	$simplate->force_compile = $forceCompile;
	$simplate->template_dir = '../../templates';
	$simplate->compile_dir = 'template_c_sim';
	$simplate->left_delimiter = '[--';
	$simplate->right_delimiter = '--]';

	$var1 = "Hello World<><<><></br>";
	$var2 = "Asial";
	$array1 = array('日', '月', '火', '水', '木', '金', '土');
	$array3 = array('日', '月', '火', '水', '木', '金', '土');
	$array4 = array('日', '月', '火', '水', '木', '金', '土');
	$array5 = array('日', '月', '火', '水', '木', '金', '土');
	$array6 = array('日', '月', '火', '水', '木', '金', '土');

	$simplate->assign('var1', $var1);
	$simplate->assign('var2', $var2);
	$simplate->assign('array', $array1);
	$simplate->assign('array2', $array3);
	$simplate->assign('array3', $array4);
	$simplate->assign('array3', $array5);
	$simplate->assign('array3', $array6);
	$simplate->assign('array3', array('hoge'=>'aaaaaaaaa'));
	$simplate->fetch('debugm.php'); 
}
$end = microtime(true);
echo "simplate : ".($end - $start)." msec<br>";

$start = microtime(true);
for( $i=0; $i<$loopCount; ++$i ){
	$smarty = new Smarty();
	$smarty->force_compile = $forceCompile;
	$smarty->template_dir = '../../templates';
	$smarty->compile_dir = 'template_c';
	$smarty->left_delimiter = '[--';
	$smarty->right_delimiter = '--]';

	$var1 = "Hello World<><<><></br>";
	$var2 = "Asial";
	$array1 = array('日', '月', '火', '水', '木', '金', '土');
	$array3 = array('日', '月', '火', '水', '木', '金', '土');
	$array4 = array('日', '月', '火', '水', '木', '金', '土');
	$array5 = array('日', '月', '火', '水', '木', '金', '土');
	$array6 = array('日', '月', '火', '水', '木', '金', '土');

	$smarty->assign('var1', $var1);
	$smarty->assign('var2', $var2);
	$smarty->assign('array', $array1);
	$smarty->assign('array2', $array3);
	$smarty->assign('array3', $array4);
	$smarty->assign('array3', $array5);
	$smarty->assign('array3', $array6);
	$smarty->assign('array3', array('hoge'=>'aaaaaaaaa'));
	$smarty->fetch('debugm.php'); 
}
$end = microtime(true);
echo "smarty : ".($end - $start)." msec<br>";


テンプレートファイル

<html>
  <head></head>
  <body>
  <p>
  [-- $var1 --]
  [--foreach from=$array key=key item=value name=array --]
  [--$key--]:[--$value--]<br/>
  <br/>
  [--/foreach--]
  [-- $array2.hoge --]

  [--foreach from=$array3 key=key item=value name=array --]
  [--$key--]:[--$value--]<br/>
  <br/>
  [--/foreach--]

  [--foreach from=$array4 key=key item=value name=array --]
  [--$key--]:[--$value--]<br/>
  <br/>
  [--/foreach--]

  [--foreach from=$array5 key=key item=value name=array --]
  [--$key--]:[--$value--]<br/>
  <br/>
  [--/foreach--]

  [--foreach from=$array6 key=key item=value name=array --]
  [--$key--]:[--$value--]<br/>
  <br/>
  [--/foreach--]
  </p>
  <hr/>

Simplate VS Smartyの結果

今の環境で実行した結果、Smartyの方がSimplateよりも2倍早かった(VMWareの時間は精神と時の部屋状態なので計測値はあてにならない)
実行順序を変えても変わらなかったので、Simplateの方が遅いのだろうと判断した。


force_compileフラグをtrueにするととてもSmartyは遅いのでSimplateの方がテンプレートのコンパイルは早いことも確認できた。
また、fetchメソッドをコメントアウトするとSimplateの方が早いことも確認できた。


最終的にはcompile_checkをfalseにするだけでも十分な効果が確認できたのでSmartyでも十分使えるじゃないかという話にまとまった。

memcachedを使う

次にファイルのIOを無くせば早いんじゃないかと思って、memcachedをインストールして数値を取ってみた。
PHPで使うにはPECLmemcachedのライブラリがあるのでそれを使えば実行ができる。しかし、php-libmemcachedの速度調査 get()とset()編によると
php-libmemcachedというのを使うと早いとあったので、こっちを使うことに決めた。


いくつか参考にしたページ一覧

memcachedで実験

無事GREEのサンプルコードが実行でき、memcachedが動いていることが確認できたのでSmartyをパワーアップさせることが出来るかどうか試すことにした。

Smartyコンパイル済みファイルをmemcachedに乗っけてしまえばFileIOがなくなって幸せになれるんじゃないか???
という仮説の検証のために下記のコードを実行した。Smartyを継承したクラスを作り、そこにmemcachedからデータを取れるようなら取るように書き換えた。(今回はfetchするだけの実験をしたのでfetchだけ改造した)

注意しなければいけないのはeval関数。Smartyコンパイル済みテンプレートファイルはinclude関数を実行したときに一発で出力できるようにPHPタグを含んでいるのでそのまま実行するとParse Errorで怒られる。
eval関数のドキュメントを見ているとPHPの終了タグを含めることができるとわかったので、
evalに渡す文字列にPHP終了タグを付与して実行した。

実験に使ったコード

class SmartyExt extends Smarty{
    function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false){

        $assignMemchached = false;
        
        $memcached = new Memcached();
        $memcached->addserver('localhost', 11211);
        
        $_smarty_compile_path = $this->_get_compile_path($resource_name);
        ob_start();
        if ($this->_is_compiled($resource_name, $_smarty_compile_path)
                || $this->_compile_resource($resource_name, $_smarty_compile_path))
        {
            if( $cache = $memcached->get($resource_name) ){
                eval( "?>".$cache );
            }else{
                include($_smarty_compile_path);
                $assignMemchached = true;
                if( $assignMemchached )
                    $memcached->set($resource_name, file_get_contents($_smarty_compile_path));
            }
        }
        $_smarty_results = ob_get_contents();
        ob_end_clean();
        
        return $_smarty_results;
    }
}

memcachedを使ったSmarty強化作戦結果

なぜか今までよりも3割遅くなった。しかし、VMWare上なので具体的な数値は当てにならないと思われる。
include関数はFileIOがあるはずなのになんで遅いんだ!と思っていたのだが、eval自体が遅いのではないかと思っている。
多くのベンチ結果ではSmartyテンプレートのようなPHPタグを複数含む例がないので、複数あるときには遅いのでは?という仮説を立てた。

まとめ

  • Simplateコンパイルはめちゃめちゃ早いがfetchが遅い
  • includeよりもevalは遅いときがある?


たまたま今の環境でそうなるけども、他の環境ではそうならない可能性が否定できない。
実環境でも実行しないと真相はわからないかも・・・