#!/usr/bin/perl -w =head1 NAME bench_various_templaters.pl - test the relative performance of several different types of template engines. =cut use strict; use Benchmark qw(timethese cmpthese); my $file = $0; $file =~ s|[^/]+$|WrapEx.pm|; #require $file; use Template; use Template::Stash; use Template::Stash::XS; use Text::Template; use HTML::Template; use HTML::Template::Expr; use HTML::Template::JIT; use CGI::Ex::Dump qw(debug); use CGI::Ex::Template; use CGI::Ex::Template::XS; use POSIX qw(tmpnam); use File::Path qw(mkpath rmtree); my $dir = tmpnam; my $dir2 = "$dir.cache"; mkpath($dir); mkpath($dir2); END {rmtree $dir; rmtree $dir2}; my @dirs = ($dir); my $form = { foo => 'bar', pass_in_something => 'what ever you want', }; ###----------------------------------------------------------------### my $stash_t = { shell_header => "This is a header", shell_footer => "This is a footer", shell_start => "", shell_end => "", a_stuff => [qw(one two three four)], }; my $stash_ht = { shell_header => "This is a header", shell_footer => "This is a footer", shell_start => "", shell_end => "", a_stuff => [map {{name => $_}} qw(one two three four)], }; $FOO::shell_header = $FOO::shell_footer = $FOO::shell_start = $FOO::shell_end = $FOO::a_stuff; $FOO::shell_header = "This is a header"; $FOO::shell_footer = "This is a footer"; $FOO::shell_start = ""; $FOO::shell_end = ""; $FOO::a_stuff = [qw(one two three four)]; ###----------------------------------------------------------------### ### TT style template my $content_tt = q{[% shell_header %] [% shell_start %] [% IF foo %] This is some text. [% END %] [% FOREACH i IN a_stuff %][% i %][% END %] [% pass_in_something %] [% shell_end %] [% shell_footer %] }; if (open (my $fh, ">$dir/foo.tt")) { print $fh $content_tt; close $fh; } ###----------------------------------------------------------------### ### HTML::Template style my $content_ht = q{ This is some text. }; if (open (my $fh, ">$dir/foo.ht")) { print $fh $content_ht; close $fh; } ###----------------------------------------------------------------### ### Text::Template style template my $content_p = q{{$shell_header} {$shell_start} { if ($foo) { $OUT .= " This is some text. "; } } { $OUT .= $_ foreach @$a_stuff; } {$pass_in_something} {$shell_end} {$shell_footer} }; ###----------------------------------------------------------------### ### setup the objects my $tt = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash->new($stash_t), }); my $ttx = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash::XS->new($stash_t), }); my $ct = CGI::Ex::Template->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, }); my $ctx = CGI::Ex::Template::XS->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, }); my $pt = Text::Template->new(TYPE => 'STRING', SOURCE => $content_p, HASH => $form); my $ht = HTML::Template->new(type => 'scalarref', source => \$content_ht); $ht->param($stash_ht); $ht->param($form); my $hte = HTML::Template::Expr->new(type => 'scalarref', source => \$content_ht); $hte->param($stash_ht); $hte->param($form); my $ht_c = HTML::Template->new(type => 'filename', source => "foo.ht", cache => 1, path => \@dirs); $ht_c->param($stash_ht); $ht_c->param($form); my $ht_j = HTML::Template::JIT->new(filename => "foo.ht", path => \@dirs, jit_path => $dir2); $ht_j->param($stash_ht); $ht_j->param($form); ###----------------------------------------------------------------### ### make sure everything is ok by trying it once my $out_tt = ""; $tt->process(\$content_tt, $form, \$out_tt); my $out_ttx = ""; $ttx->process(\$content_tt, $form, \$out_ttx); my $out_ct = ""; $ct->process(\$content_tt, $form, \$out_ct); my $out_ctx = ""; $ctx->process(\$content_tt, $form, \$out_ctx); my $out_c2 = ""; $ct->process('foo.tt', $form, \$out_c2); my $out_c3 = ''; $ct->process_simple(\$content_tt, {%$stash_t, %$form}, \$out_c3); my $out_pt = $pt->fill_in(PACKAGE => 'FOO', HASH => $form); my $out_ht = $ht->output; my $out_hte = $hte->output; my $out_htc = $ht_c->output; my $out_htj = $ht_j->output; if ($out_ct ne $out_tt) { debug $out_ct, $out_tt; die "CGI::Ex::Template didn't match tt"; } if ($out_ctx ne $out_tt) { debug $out_ctx, $out_tt; die "CGI::Ex::Template::XS didn't match tt"; } if ($out_ttx ne $out_tt) { debug $out_ttx, $out_tt; die "Template::Stash::XS didn't match tt"; } if ($out_c2 ne $out_tt) { debug $out_c2, $out_tt; die "CGI::Ex::Template from file didn't match tt"; } if ($out_c3 ne $out_tt) { debug $out_c3, $out_tt; die "CGI::Ex::Template by swap didn't match tt"; } if ($out_pt ne $out_tt) { debug $out_pt, $out_tt; die "Text Template didn't match tt"; } if ($out_ht ne $out_tt) { debug $out_ht, $out_tt; die "HTML::Template didn't match tt"; } if ($out_hte ne $out_tt) { debug $out_hte, $out_tt; die "HTML::Template::Expr didn't match tt"; } if ($out_htc ne $out_tt) { debug $out_htc, $out_tt; die "HTML::Template::Expr didn't match tt"; } if ($out_htj ne $out_tt) { debug $out_htj, $out_tt; die "HTML::Template::JIT didn't match tt"; } ###----------------------------------------------------------------### my $tests = { TT_str => sub { my $tt = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash->new($stash_t), }); my $out = ""; $tt->process(\$content_tt, $form, \$out); }, TT_mem => sub { my $out = ""; $tt->process('foo.tt', $form, \$out); }, TT_compile => sub { my $tt = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash->new($stash_t), COMPILE_DIR => $dir2, }); my $out = ""; $tt->process('foo.tt', $form, \$out); }, TTX_str => sub { my $tt = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash::XS->new($stash_t), }); my $out = ""; $tt->process(\$content_tt, $form, \$out); }, TTX_mem => sub { my $out = ""; $ttx->process('foo.tt', $form, \$out); }, TTX_compile => sub { my $tt = Template->new({ INCLUDE_PATH => \@dirs, STASH => Template::Stash::XS->new($stash_t), COMPILE_DIR => $dir2, }); my $out = ""; $tt->process('foo.tt', $form, \$out); }, CET_str => sub { my $ct = CGI::Ex::Template->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, }); my $out = ""; $ct->process(\$content_tt, $form, \$out); }, CET_mem => sub { my $out = ""; $ct->process('foo.tt', $form, \$out); }, CET_compile => sub { my $ct = CGI::Ex::Template->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, COMPILE_DIR => $dir2, }); my $out = ''; $ct->process('foo.tt', $form, \$out); }, CTX_str => sub { my $ct = CGI::Ex::Template::XS->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, }); my $out = ""; $ct->process(\$content_tt, $form, \$out); }, CTX_mem => sub { my $out = ""; $ctx->process('foo.tt', $form, \$out); }, CTX_compile => sub { my $ct = CGI::Ex::Template::XS->new({ INCLUDE_PATH => \@dirs, VARIABLES => $stash_t, COMPILE_DIR => $dir2, }); my $out = ''; $ct->process('foo.tt', $form, \$out); }, TextTemplate => sub { my $pt = Text::Template->new( TYPE => 'STRING', SOURCE => $content_p, HASH => $form); my $out = $pt->fill_in(PACKAGE => 'FOO', HASH => $form); }, HT_str => sub { my $ht = HTML::Template->new(type => 'scalarref', source => \$content_ht); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, HT_mem => sub { my $ht = HTML::Template->new(type => 'filename', source => "foo.ht", path => \@dirs, cache => 1); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, HT_compile => sub { my $ht = HTML::Template->new(type => 'filename', source => "foo.ht", file_cache => 1, path => \@dirs, file_cache_dir => $dir2); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, HTE_str => sub { my $ht = HTML::Template::Expr->new(type => 'scalarref', source => \$content_ht); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, HTE_mem => sub { my $ht = HTML::Template::Expr->new(type => 'filename', source => "foo.ht", path => \@dirs, cache => 1); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, HTJ_compile => sub { my $ht = HTML::Template::JIT->new(filename => "foo.ht", path => \@dirs, jit_path => $dir2); $ht->param($stash_ht); $ht->param($form); my $out = $ht->output; }, }; my %mem_tests = map {($_ => $tests->{$_})} qw(TT_mem TTX_mem CET_mem HT_mem HTE_mem CTX_mem); my %cpl_tests = map {($_ => $tests->{$_})} qw(TT_compile TTX_compile CET_compile HT_compile HTJ_compile CTX_compile); my %str_tests = map {($_ => $tests->{$_})} qw(TT_str TTX_str CET_str HT_str HTE_str TextTemplate CTX_str); print "------------------------------------------------------------------------\n"; print "From a string or scalarref tests\n"; cmpthese timethese (-2, \%str_tests); print "------------------------------------------------------------------------\n"; print "Compiled and cached on the file system tests\n"; cmpthese timethese (-2, \%cpl_tests); print "------------------------------------------------------------------------\n"; print "Cached in memory tests\n"; cmpthese timethese (-2, \%mem_tests); print "------------------------------------------------------------------------\n"; print "All variants together\n"; cmpthese timethese (-2, $tests); ###----------------------------------------------------------------###