2016年01月21日
DynamicMTML(ダイナミックパブリッシング) で動的インクルードを使うと Smartyのコンパイルキャッシュの効果が限定的になる
最近 PowerCMS や MTのダイナミックパブリッシングで大きなモジュールをインクルードすると思うようなスピードが出ないケースに遭遇しました。調査にあたっては SpeedMeterプラグインを利用しました。
モジュール インクルードサンプル の中では以下のように前後半を分けて、どの部分が時間を要しているのかを調査しようとしました。
インクルード元
<mt:SpeedMeter name="モジュール全体">
<mt:Include module="インクルードサンプル">
</mt:SpeedMeter>
モジュール内
<mt:SpeedMeter name="モジュール前半">
# 大量のMTMLを含むテンプレートのコード
</mt:SpeedMeter>
<mt:SpeedMeter name="モジュール後半">
# 大量のMTMLを含むテンプレートのコード
</mt:SpeedMeter>
こうしてテンプレートのビルドブロックごとのビルド時間を計測していったのですが、不思議な数値が出たのです。
- 'モジュール全体'をビルドしました。処理時間: 2.50459890365601
- 'モジュール後半'をビルドしました。処理時間: 0.00037813186645508
- 'モジュール前半'をビルドしました。処理時間: 0.19418096542358
前後半をあわせて0.2秒にも満たないのにテンプレート全体では2.5秒もかかっています。大きなテンプレートをSQLでロードするのに時間がかかっているのかと疑い、ファイルインクルードにしましたが速くなったのは誤差程度。
そこで、mt:Includeのコードを確認しました。
mt/php/lib/function.mtinclude.php
ob_start();
$ctx->_eval('?>' . $_var_compiled);
$_contents = ob_get_contents();
ob_end_clean();
ここでピンと来たのですが、MTのダイナミックパブリッシング(DynamicMTMLも基本は同じ)では、以下の順で処理が行なわれます。
- .htaccessによってリクエストが .mtview.php に渡される
- DynamicMTML では addons/DynamicMTML.pack/php/dynamicmtml.run.php 経由で、MTでは直接 php/mt.php が処理を担う
- プラグインの初期化や設定情報等を初期化(一部DBへのアクセス有り)
- mt_fileinfoレコードを検索し、必要なオブジェクトをデータベースから取得し、ページのコンテキストをセット
- テンプレートをロードし、MTMLを取得する
- MTViewer.php がテンプレートをビルドする
class MTViewer(MTViewer.php)は PHPのテンプレートエンジン Smartyの拡張クラスで、prefilter.mt_to_smarty.php の中の smarty_prefilter_mt_to_smarty 関数がテンプレートの処理の冒頭に呼ばれます。この処理は MTML(MTタグ)を正規表現によってパースし、Smartyのコンパイル形式に変換し、その後ページの処理が行なわれます。
$this->load_filter('pre', 'mt_to_smarty');
Smarty のコンパイル処理は PHP のネイティブコードを生成し、各ウェブサイト/ブログ以下の templates_c/ ディレクトリにコンパイル済みの状態で保存されます。一度作成してしまえば、その後はコンパイルされた PHP スクリプトが実行され、テンプレートに更新があった時のみ再コンパイルされます。
動的MTInclude ではコンパイル結果はキャッシュされない
このケースの 2.X秒はこのコンパイル処理だったのでした。数千行ある巨大なMTタグを含むモジュールを MTIncludeしていたので、その部分が毎回コンパイルされていたというわけです。
対策はモジュールを静的インクルードすること
対策はモジュールを静的インクルードすることです。以下は改修前のコンパイルキャッシュです。MTInclude タグのみがキャッシュされています。
モジュールの中を以下のように変更します。
<mt:Unless regex_replace="/^\s*\n/gm","" trim="1">
<mt:DynamicMTML>
# モジュールの中身
</mt:DynamicMTML>
</mt:Unless>
インクルード元のテンプレートの下記の部分を変更します。
<mt:DynamicMTML>
<mt:Include module="インクルードサンプル">
</mt:DynamicMTML>
これを、静的インクルードにします。
<mt:Include module="インクルードサンプル">
これで、MTInclude タグの中身が展開された上でキャッシュされました。
これでページの表示速度が大きく改善しました。複雑で巨大なMTタグを含むテンプレートモジュールをダイナミックでインクルードしている時には意識すると良いと思います。但し、コンパイルキャッシュのサイズが 1MBを超えるなど、巨大になってしまうと今度はまた逆に速度低下の要因となります。
テンプレートをコンパクトにしつつ、コンパイルキャッシュの恩恵をうけるために 静的インクルードを使う、というのが今回のポイントでした。
コメントを投稿する