アルファサード株式会社 代表取締役 野田 純生のブログ


MTIncludeは遅いのか?


公開日 : 2010-06-11 14:36:27


結論を先に書きます。遅くない。遅い場合についてもMTIncludeタグ自身の影響は些少。

MTのスタティック・パブリッシングにおける再構築とはつまりこういうことかと思います。

  1. テンプレートをロードしてコンテクストをセットする。
  2. 各タグ内で必要に応じてデータを取得したりコンテクストをセットし、
  3. ブロック(テンプレート)をコンパイルしてビルドしていく。
  4. この時インクルードされているテンプレート(モジュールやウィジェット)があればロードして展開する。

詳しくはlib/MT/Template/WeblogPublisher.pm (lib/MT/Template/Builder.pm とか、あと各テンプレートタグについては lib/MT/Template/ContextHandlers.pmなんか)を見れば理解できると思います。

さて、上記の4番目のロードとは、SQLを発行してデータベースからテンプレートを読み込むことを指します。SELECT * FROM `mt_template` WHERE template_blog_id=2 AND template_name='サイドバー' AND template_type='custom' みたいなやつです。

ですので、当然ながらフラット化されたテンプレートと比較すると遅くなる、重くなると思われがちで、一見この指摘は正しいように思えます。それがMTIncludeが重いといわれる原因でしょうか?

実際にやってみました。

  • 新規にMTをインストール。標準以外のプラグインはなし。
  • クラッシックウェブサイト以下にクラシックブログを作成。
  • このブログをエクスポートしたデータをインポート(エントリー数は588)。
  • ブログ記事の再構築を行う(5回計測。平均3分46.0秒)

3分43秒 / 3分44秒 / 3分48秒 / 3分49秒 / 3分46秒

さて、ブログ記事テンプレートの「サイドバー」モジュールをブログ記事テンプレートの該当箇所にそのまま貼付けて再構築を行いました。MTIncludeを使わないパターンです。結果...

3分55秒 / 3分53秒 / 3分49秒 / 3分47秒 / 3分51秒

変わんなかった(5回計測。平均3分49.2秒)。むしろ遅い?変わらないのはともかく遅くなった原因はわかんないです。が、まぁ誤差の範囲でしょう。

但し、以下の事実は知られていないかもしれないので書いておきます。

インクルードしているテンプレートモジュール(ウィジェット)のロードは1リクエストにつき1回だけ

具体的にはlib/MT/Template/ContextHandlers.pm の3598行目。MT::Requestにキャッシュされていない時だけロードされます。

ちなみに1リクエストというのは、mt.cgiへのリクエストです。MTのブログ記事の再構築は1度のリクエストで環境変数 EntriesPerRebuild の設定値(初期値は40)ずつ実行されます。40ファイル再構築後はリダイレクトして再び40ファイルの再構築を(これをエントリ数を40で割った回数分)繰り返すわけです。 588ファイルの再構築であれば、リクエストは15回です。EntriesPerRebuildの値を100にすればリクエストはわずか6回。つまり、テンプレートをロードするためのSQLのリクエストは100ファイルの再構築時にわずか(インクルードしているモジュールが1つあたり) 1   6 回です。

つまり、インクルードしていることの影響は些少といえます。

さて次。コンパイルです。ビルドする前にコンパイルします。MTIgnore云々の話はこのコンパイル時の話になります。計測のため(というか本当に速くなるんだったらいいなと思って)MTCompileCacheBlockというタグを作ってテストします。


sub _hdlr_compile_cache {
    my ( $ctx, $args, $cond ) = @_;
    require MT::Request;
    my $key = $args->{ key };
    my $id = "compile_cache_$key";
    my $r = MT::Request->instance;
    my $cache = $r->cache( $id );
    my $builder = $ctx->stash( 'builder' );
    my $tokens;
    if ( $cache ) {
        $tokens = $cache;
    } else {
        my $tmpl = $ctx->stash( 'uncompiled' ) || '';
        $tokens = $builder->compile( $ctx, $tmpl );
        $r->cache( $id, $tokens );
    }
    my $res = $builder->build( $ctx, $tokens );
    return $res;
}

テンプレートは以下のようにモジュールを囲む形で書きます。



    <$mt:Include module="サイドバー"$>

3分51秒 / 3分51秒 / 3分46秒 / 3分46秒 / 3分47秒

3分48.2秒。これも...誤差の範囲じゃないですか?

高速化云々の話をする時には「どこがツボ」かを考えないと無駄な努力に終わる可能性が高い。

ツボは何? そう、ツボは「ビルド」です。テンプレートキャッシュが有効なのはキャッシュした部分のテンプレートをビルドする処理を2回目以降スキップできるから劇的な効果が見込めるわけです。

では何故MTIncludeが重い(遅い)と(しばしば)指摘されるのか?

本当のところはわかりません。推測ですがおそらく「最近のブログ記事」とか「最近のコメント」等の相応に負荷のかかるブロックタグを利用したブロックがモジュール化されているからじゃないかという気がしますが。確かにそういった場合にキャッシュしていない場合に全体的に重くなることは事実で、でもそれはMTIncludeのせいではなくて各ページで共通の結果を出力する部分をキャッシュせずに処理していることが問題なわけです。MT3の頃だったか、モジュールをインデックス・テンプレートにして吐き出しておいてファイルとしてインクルードするようなTipsがあったかと思いますが、確かに劇的に効果的にはたらくケースもあります。

MT4(.xだったか記憶曖昧ですが)から実装されたテンプレートモジュールのキャッシュを使えば一定時間はビルドされずキャッシュが使われますからこれは効果があります。但し、再構築中に間違いに気づいてブログ記事を削除したりしたときに「最新のブログ記事」リストに削除したものが残ってしまうといった問題がないわけではないので、リクエスト毎にキャッシュを生成するようなプラグインなどはお手軽で、データベースでなくメモリへのキャッシュですので(最初の1回しか)クエリも発行されないといったメリットがあり劇的に軽量化できます。

ということで、フロントエンジニアの人はMTIgnoreもMTIncludeも積極的に使って可読性が高くメンテナビリティに優れたテンプレートを書くことに集中すれば良いと思うのです。思うのでした。

追記

モジュールをフラット化した状態でCompile結果をキャッシュして実測。

4分1秒 / 3 分52秒 / 以下省略

カテゴリ


このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の創業者であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ