Template Toolkit 探訪 (2)
前回お話したように,Template::Provider は,コンパイルしたテンプレートオブジェクトを,デフォルトではメモリ(内部のハッシュ)に蓄えておき,出来る限り再利用しています。ただ,当然再利用するためにはテンプレート「自体は」静的なものでなくてはいけませんから,ファイル名が指定された場合のみキャッシュします。つまり,生データのスカラハッシュや,ファイルグロブを与えた場合は,毎回コンパイルしているわけです。
デフォルトでは読み込まれれば読み込まれただけキャッシュにためておきますが,CACHE_SIZE というパラメータを初期化時に指定しておくと,その数のテンプレートだけ保持します。4 と指定しておくと,4つぶんだけキャッシュして,5つめがきたときには,一番使われていない(LRU)エントリを削除します。
さて,今回の本題です。Template::Provider には,メモリだけでなく,ディスクにキャッシュするという機能があります。COMPILE_DIR や COMPILE_EXT という初期化パラメータを指定すると,コンパイルしたテンプレートオブジェクトを指定されたディレクトリに書き込みます。一つのテンプレートファイルにつき,一つのキャッシュファイルが作成されます。一つのテンプレートに複数ブロックがあったとしても,それらはまとめて記録されているわけです。
では,メモリにキャッシュするのとディスクにキャッシュするのとでは,どちらが望ましいのでしょうか。mod_perl 環境で ab によるベンチマークをとってみました。
- VMWare Server 上の仮想ゲストの Fedora core 5, 384MB
- Apache 2.2 + mod_perl 2 / Perl 5.8.8
- テストの度に,service httpd restart
- ab -n 1000 -c 10 でリモートから実行
- KeepAlive Off, MaxClients 8, MaxRequestsPerChild 128
- テンプレートファイルは 5 つで,INCLUDE したり WRAPPER したりしている
- でも最終的な出力は 500 bytes 弱
- Template::Provider::Encoding と Template::Stash::ForceUTF8 を利用,出力時に Encode::encode('utf8') で UTF-8 に変換
のような環境です(あいかわらず詳細なソースがなくてすみません)。各テンプレートファイルのサイズが小さいので,やや disk cache が不利かなという予想です。あと,MaxRequestsPerChild はもっと小さくしておけばよかった…
結果のうち主要な部分を以下に示します。
rps | connection times (ms) | |||||
---|---|---|---|---|---|---|
永続化 | キャッシュ | min | mean | median | max | |
なし | mem | 20.00 | 38 | 487 | 165 | 41906 |
disk | 29.39 | 63 | 334 | 141 | 25632 | |
あり | mem | 39.77 | 34 | 242 | 82 | 17161 |
disk | 47.73 | 47 | 203 | 68 | 20952 |
一番成績がよかったものを太字にしてあります。また,接続所要時間の最小値と最大値があまりに離れているので,mean より median のほうが平均としては即していると思います。ちなみに,永続化ありというのは,Templete オブジェクトをインスタンス変数として保持しておき,httpd プロセスが存続する限り使い回す,というモデルで書いたコードです*1。
上記の結果から,
- コンパイルしたコードをディスクに書き込んだり,修正時刻を検証したりする手間が増えるので,接続所要時間の最小値と最大値は,memory cache のほうが成績がよい。
- しかし,平均的な性能は disk cache のほうが memory cache よりよくなる。
- テンプレートオブジェクトのインスタンスは,できるだけ再利用したほうがよい。
ことがわかります。永続化なし-disk cache と,永続化あり-mem cache が,もうすこし拮抗してくれたほうが面白かったんですが,永続化が圧倒的に強く効いていますね。
パフォーマンスをあげるために Template オブジェクトを再利用しているけど,テンプレートキャッシュで httpd のプロセスが肥大化してしまうのが不安,という向きは,Template オブジェクト自体の永続化は避け,ディスクキャッシュをするようにするのもよいのではないでしょうか。
という結論を本当は書きたかったんですが,実際はメモリと時間のトレードオフになっているのでケースバイケースですね。メモリとディスクに余裕があれば,永続化あり&ディスクキャッシュが最強,ということで。
ちなみに,ディスクキャッシュを有効にしていても,メモリキャッシュは有効になります。
おまけ
ディスクキャッシュされたファイル自体は,Perl のスクリプトそのものです。そのファイルを覗いていたんですが,Template::Provider::Encoding で UTF-8 on なテンプレートとして扱うと,きちんと先頭が「use utf8;」ってなるんですよ。Template::Document にそのあたりがちゃんとインプリメントされているんですが,感動しました。
*1:いわゆる永続化とは違いますが大目にみてください