PowerCMS ブログ

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も基本は同じ)では、以下の順で処理が行なわれます。

  1. .htaccessによってリクエストが .mtview.php に渡される
  2. DynamicMTML では addons/DynamicMTML.pack/php/dynamicmtml.run.php 経由で、MTでは直接 php/mt.php が処理を担う
  3. プラグインの初期化や設定情報等を初期化(一部DBへのアクセス有り)
  4. mt_fileinfoレコードを検索し、必要なオブジェクトをデータベースから取得し、ページのコンテキストをセット
  5. テンプレートをロードし、MTMLを取得する
  6. 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 スクリプトが実行され、テンプレートに更新があった時のみ再コンパイルされます。

templates_c ディレクトリに保存されたコンパイルキャッシュ

動的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を超えるなど、巨大になってしまうと今度はまた逆に速度低下の要因となります。

テンプレートをコンパクトにしつつ、コンパイルキャッシュの恩恵をうけるために 静的インクルードを使う、というのが今回のポイントでした。

カテゴリー
DynamicMTMLテンプレート作成Tipsトラブルシューティング

ページの先頭へ