oinume journal

Scratchpad of what I learned

Text::MicroTemplateの速度を簡単にベンチマーク

2009/11/15 追記tokuhiromさんのコメントを受けて、Text::MicroTemplate以外のテンプレートファイルにHTMLエスケープの処理を追加してベンチマークを取り直しています。

 

Perlのテンプレートエンジンでどれを使おうか悩んでいるので、Text::MicroTemplateをはじめとする以下のテンプレートエンジンの速度を簡単にベンチマークしてみました。

下記の要因で単純に比較できない部分もあるので、これを鵜呑みにしないでなるべく自身のユースケースの延長でもベンチマークを取ることをお奨めします。あくまで参考値ということで。

  • そもそも持っている機能が全然違うので単純に比較できない
  • 実際のテンプレートファイルはもっと複雑なはず
  • utf8 flagの処理入れる場合もある

ではとりあえず結果から。

 

キャッシュなし

 

$ perl template_speed_file.pl 3000 0

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 5 wallclock secs ( 4.60 usr + 0.09 sys = 4.69 CPU) @ 639.66/s (n=3000)

HT::Compiled: 10 wallclock secs (10.56 usr + 0.10 sys = 10.66 CPU) @ 281.43/s (n=3000)

HT::Pro: 1 wallclock secs ( 0.20 usr + 0.04 sys = 0.24 CPU) @ 12500.00/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 2 wallclock secs ( 2.39 usr + 0.07 sys = 2.46 CPU) @ 1219.51/s (n=3000)

TT: 10 wallclock secs ( 9.65 usr + 0.17 sys = 9.82 CPU) @ 305.50/s (n=3000)

Rate HT::Compiled TT HT MT HT::Pro

HT::Compiled 281/s -- -8% -56% -77% -98%

TT 305/s 9% -- -52% -75% -98%

HT 640/s 127% 109% -- -48% -95%

MT 1220/s 333% 299% 91% -- -90%

HT::Pro 12500/s 4342% 3992% 1854% 925% --

 

 

キャッシュあり

コードを見てもらえばわかるのですが、「キャッシュあり」とは具体的には

  • テンプレートのインスタンスを作るのは最初の1回だけ(キャッシュなしの場合は毎回作っている)
  • テンプレートエンジンに対して、「キャッシュあり」とコンストラクタで指令

ということになります。

$ perl template_speed_file.pl 3000 1

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 3 wallclock secs ( 2.69 usr + 0.00 sys = 2.69 CPU) @ 1115.24/s (n=3000)

HT::Compiled: 0 wallclock secs ( 0.28 usr + 0.00 sys = 0.28 CPU) @ 10714.29/s (n=3000)

(warning: too few iterations for a reliable count)

HT::Pro: 0 wallclock secs ( 0.13 usr + 0.02 sys = 0.15 CPU) @ 20000.00/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 1 wallclock secs ( 0.22 usr + 0.01 sys = 0.23 CPU) @ 13043.48/s (n=3000)

(warning: too few iterations for a reliable count)

TT: 1 wallclock secs ( 1.84 usr + 0.00 sys = 1.84 CPU) @ 1630.43/s (n=3000)

Rate HT TT HT::Compiled MT HT::Pro

HT 1115/s -- -32% -90% -91% -94%

TT 1630/s 46% -- -85% -87% -92%

HT::Compiled 10714/s 861% 557% -- -18% -46%

MT 13043/s 1070% 700% 22% -- -35%

HT::Pro 20000/s 1693% 1127% 87% 53% --

  • HTML::Template::Proが爆速!
  • Text::MicroTemplateは、キャッシュなしだとそこそこ、キャッシュありはかなり速い部類に入るようです。キャッシュありだとHTML::Template::Proには及ばないものの、かなり速いです。
  • HTML::Template::Compiledはキャッシュを無効にするとTTより遅いという結果ですが、キャッシュを有効にすると猛烈に速くなるようです。ソースが追えてないのですが、中でガチンコにキャッシュしてるのでしょうか。
  • たぶん一番使われているTTはよく「遅い」と言われていますが、キャッシュを有効にすると意外にもHTML::Templateより速くなりました。

というのがポイントだと思います。この結果だけで判断すると、HTML::Template::Proは神がかり的な速さなのですが、そもそもPerlのコードをテンプレートに記述できず自由度は低いので(*1)、使う場合にはそれなりに不便さを覚悟した方がよいと思います。「便利さ」という点だとText::MicroTemplateはテンプレート内にPerlのコードが書ける分かなり自由度が高い、かつ速度もいい感じでバランスが取れていると言えます。

 

*1) Perlのコードをテンプレートに書けてしまっていいのかどうかは使う人次第だと思いますが

 

以下にソースを載せておきます。テストに使ったマシンはUbuntu 9.04, perl 5.10.0, CPUが Athlon Dual Core Processor 5050e です。

 

template_speed_file.pl

 

#!/usr/bin/env perl

 

use strict;

use warnings;

use HTML::Template;

use HTML::Template::Compiled speed => 1;

use HTML::Template::Pro;

use Template;

use Text::MicroTemplate::File;

 

use Benchmark qw(timethese cmpthese);

 

my @LANGUAGES = (

{ language => 'Perl', ll => 1 },

{ language => 'Ruby', ll => 1 },

{ language => 'Python', ll => 1 },

{ language => 'PHP', ll => 1 },

{ language => 'Java' },

{ language => 'C' },

{ language => 'C++' },

{ language => 'C#' },

{ language => 'VB' },

{ language => 'VB.NET' },

{ language => 'ASP.NET' },

{ language => 'Delphi' },

{ language => 'Erlang' },

{ language => 'Scala', ll => 1 },

{ language => 'Go' },

);

my $count = shift @ARGV || 1000;

my $cache = shift @ARGV || 0;

 

my $mt = undef;

sub mt {

my %args = (use_cache => $cache);

if ($cache) {

$mt ||= Text::MicroTemplate::File->new(%args);

} else {

$mt = Text::MicroTemplate::File->new(%args);

}

 

my $s = $mt->render_file(

'template_speed_file.mt',

{

page_title => 'LL Programming languages',

languages => @LANGUAGES,

}

)->as_string;

}

 

my $ht = undef;

sub ht {

my %args = (

filename => 'template_speed_file.ht',

case_sensitive => 1,

die_on_bad_params => 0,

cache => $cache,

);

if ($cache) {

$ht ||= HTML::Template->new(%args);

} else {

$ht = HTML::Template->new(%args);

}

$ht->param(

page_title => 'LL Programming languages',

languages => @LANGUAGES,

);

my $s = $ht->output;

}

 

my $htc = undef;

sub htc {

my %args = (

filename => 'template_speed_file.ht',

case_sensitive => 1,

die_on_bad_params => 0,

cache => $cache,

);

if ($cache) {

$htc ||= HTML::Template::Compiled->new(%args);

} else {

$htc = HTML::Template::Compiled->new(%args);

}

$htc->param(

page_title => 'LL Programming languages',

languages => @LANGUAGES,

);

my $s = $htc->output;

}

 

my $htp = undef;

sub htp {

my %args = (

filename => 'template_speed_file.ht',

case_sensitive => 1,

die_on_bad_params => 0,

cache => $cache,

);

if ($cache) {

$htp ||= HTML::Template::Pro->new(%args);

} else {

$htp = HTML::Template::Pro->new(%args);

}

$htp->param(

page_title => 'LL Programming languages',

languages => @LANGUAGES,

);

my $s = $htp->output;

}

 

my $tt = undef;

sub tt {

if ($cache) {

$tt ||= Template->new(

CACHE_SIZE => $cache ? undef : 0,

);

} else {

$tt = Template->new;

}

$tt->process(

'template_speed_file.tt',

{

page_title => 'LL Programming languages',

languages => @LANGUAGES,

},

my $out,

) or die $tt->error() . "n";

}

 

# main

my $comp = timethese(

$count,

{

'MT' => &mt,

'HT' => &ht,

'HT::Pro' => &htp,

'HT::Compiled' => &htc,

'TT' => &tt,

}

);

cmpthese $comp;

 

 

template_speed_file.mt - Text::MicroTemplate用のファイル

 

? my $p = $_[0];

 

Programming languages

 

 

 

 

? for my $language (@{ $p->{languages} }) {

 

? }

 

 

 

 

 

template_speed_file.ht - HTML::Template::*用のファイル

 

 

Programming languages

 

<TMPL_VAR NAME=page_title ESCAPE=HTML>

 

 

<TMPL_LOOP NAME=languages>

 

  • <TMPL_IF NAME=ll>*<TMPL_VAR NAME=language ESCAPE=HTML>

 

 

 

 

 

 

 

template_speed_file.tt - Template::Toolkit用のファイル

 

 

Programming languages

 

[% page_title | html %]

 

 

[% FOREACH language IN languages %]

 

  • [% language.language | html %]

 

[% END %]

 

 

 

 

 

HTMLエスケープあり/なしのスピードについて

tokuhiromさんのコメントにあるように、Text::MicroTemplateは自動でHTMLエスケープがかかる一方、他のテンプレートエンジンでは手動でやる必要があるので、HTMLエスケープを入れて測り直してみました。ちなみに上に載せているベンチの結果は、HTMLエスケープを入れたものになっています。

 

特筆すべきなのは、HTML::Template::CompiledがキャッシュありかつHTMLエスケープありの場合、HTMLエスケープなしに比べてパフォーマンスがかなり劣化している点です。(その他はちょっとずつパフォーマンスが劣化していますね)。

 

キャッシュなしかつHTMLエスケープなし

 

$ perl template_speed_file.pl 3000 0

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 4 wallclock secs ( 3.85 usr + 0.10 sys = 3.95 CPU) @ 759.49/s (n=3000)

HT::Compiled: 10 wallclock secs ( 9.99 usr + 0.09 sys = 10.08 CPU) @ 297.62/s (n=3000)

HT::Pro: 1 wallclock secs ( 0.21 usr + 0.02 sys = 0.23 CPU) @ 13043.48/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 2 wallclock secs ( 2.35 usr + 0.05 sys = 2.40 CPU) @ 1250.00/s (n=3000)

TT: 8 wallclock secs ( 7.83 usr + 0.15 sys = 7.98 CPU) @ 375.94/s (n=3000)

Rate HT::Compiled TT HT MT HT::Pro

HT::Compiled 298/s -- -21% -61% -76% -98%

TT 376/s 26% -- -51% -70% -97%

HT 759/s 155% 102% -- -39% -94%

MT 1250/s 320% 233% 65% -- -90%

HT::Pro 13043/s 4283% 3370% 1617% 943% --

 

 

キャッシュなしかつHTMLエスケープあり

 

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 5 wallclock secs ( 4.60 usr + 0.09 sys = 4.69 CPU) @ 639.66/s (n=3000)

HT::Compiled: 10 wallclock secs (10.56 usr + 0.10 sys = 10.66 CPU) @ 281.43/s (n=3000)

HT::Pro: 1 wallclock secs ( 0.20 usr + 0.04 sys = 0.24 CPU) @ 12500.00/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 2 wallclock secs ( 2.39 usr + 0.07 sys = 2.46 CPU) @ 1219.51/s (n=3000)

TT: 10 wallclock secs ( 9.65 usr + 0.17 sys = 9.82 CPU) @ 305.50/s (n=3000)

Rate HT::Compiled TT HT MT HT::Pro

HT::Compiled 281/s -- -8% -56% -77% -98%

TT 305/s 9% -- -52% -75% -98%

HT 640/s 127% 109% -- -48% -95%

MT 1220/s 333% 299% 91% -- -90%

HT::Pro 12500/s 4342% 3992% 1854% 925% --

 

 

キャッシュありかつHTMLエスケープなし

 

$ perl template_speed_file.pl 3000 1

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 3 wallclock secs ( 2.50 usr + 0.00 sys = 2.50 CPU) @ 1200.00/s (n=3000)

HT::Compiled: 0 wallclock secs ( 0.15 usr + 0.00 sys = 0.15 CPU) @ 20000.00/s (n=3000)

(warning: too few iterations for a reliable count)

HT::Pro: 0 wallclock secs ( 0.10 usr + 0.04 sys = 0.14 CPU) @ 21428.57/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 0 wallclock secs ( 0.21 usr + 0.00 sys = 0.21 CPU) @ 14285.71/s (n=3000)

(warning: too few iterations for a reliable count)

TT: 1 wallclock secs ( 1.19 usr + 0.01 sys = 1.20 CPU) @ 2500.00/s (n=3000)

Rate HT TT MT HT::Compiled HT::Pro

HT 1200/s -- -52% -92% -94% -94%

TT 2500/s 108% -- -82% -87% -88%

MT 14286/s 1090% 471% -- -29% -33%

HT::Compiled 20000/s 1567% 700% 40% -- -7%

HT::Pro 21429/s 1686% 757% 50% 7% --

 

 

キャッシュありかつHTMLエスケープあり

 

Benchmark: timing 3000 iterations of HT, HT::Compiled, HT::Pro, MT, TT...

HT: 3 wallclock secs ( 2.69 usr + 0.00 sys = 2.69 CPU) @ 1115.24/s (n=3000)

HT::Compiled: 0 wallclock secs ( 0.28 usr + 0.00 sys = 0.28 CPU) @ 10714.29/s (n=3000)

(warning: too few iterations for a reliable count)

HT::Pro: 0 wallclock secs ( 0.13 usr + 0.02 sys = 0.15 CPU) @ 20000.00/s (n=3000)

(warning: too few iterations for a reliable count)

MT: 1 wallclock secs ( 0.22 usr + 0.01 sys = 0.23 CPU) @ 13043.48/s (n=3000)

(warning: too few iterations for a reliable count)

TT: 1 wallclock secs ( 1.84 usr + 0.00 sys = 1.84 CPU) @ 1630.43/s (n=3000)

Rate HT TT HT::Compiled MT HT::Pro

HT 1115/s -- -32% -90% -91% -94%

TT 1630/s 46% -- -85% -87% -92%

HT::Compiled 10714/s 861% 557% -- -18% -46%

MT 13043/s 1070% 700% 22% -- -35%

HT::Pro 20000/s 1693% 1127% 87% 53% --

 

[rakuten]book:11995194[/rakuten]