in reply to Re: I need to template this
in thread I need to template this

TT is too heavy for what you want
Sorry - this is long...

I hear the "too heavy" argument a lot as a reason to not use TT. I've provided a test script below with benchmarks that show relative speed for a highly contrived template. If TT is used in a mod_perl environment - it is faster than HT. In a non-mod_perl environment (or also in a mod_perl environment you could always use CGI::Ex::Template and then you have the full features of TT with the "lightness" of HTML::Template.

The other reason I hear is because they don't want to give too much ability to the HTML designers or because they don't want to have business logic in the templates. The first point seems roughly equivalent to SysAdmins on my network telling me I have to use Lua because they don't want me to have too many features in my programming. On the second point - there is almost nothing you can do to prevent your designers from controlling business logic from the HTML -- Javascript provides all the rope they need to hang themselves. The only option here is to ask them not to do that.

So if all the points for using H::T are really veiled non-issues - then the only real reason to pick H::T over TT or likewise to pick TT over H::T is one of religion and is generally based on what system the developer started with in the first place.

Well - anyway - the following is a script that tests the templates under different operational modes - passing in a string containing the template, passing in a file containing the template, and caching the template in memory, or the compiled template on the file system (isn't it funny that they all have essentially the same feature sets but have diffent APIs and syntax). This file is included with the CGI::Ex suite under the samples/benchmark directory. Unfortunately the copy of this file on cpan is out of date - but will include this updated file in the next release.

#!/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 CGI::Ex::Dump qw(debug); use CGI::Ex::Template; 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 => "<html>", shell_end => "<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 => "<html>", shell_end => "<end>", a_stuff => [map {{name => $_}} qw(one two three four)], }; $FOO::shell_header = $FOO::shell_footer = $FOO::shell_start = $FOO::sh +ell_end = $FOO::a_stuff; $FOO::shell_header = "This is a header"; $FOO::shell_footer = "This is a footer"; $FOO::shell_start = "<html>"; $FOO::shell_end = "<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{<TMPL_VAR NAME=shell_header> <TMPL_VAR NAME=shell_start> <TMPL_IF NAME=foo> This is some text. </TMPL_IF> <TMPL_LOOP NAME=a_stuff><TMPL_VAR NAME=name></TMPL_LOOP> <TMPL_VAR NAME=pass_in_something> <TMPL_VAR NAME=shell_end> <TMPL_VAR NAME=shell_footer> }; 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 $pt = Text::Template->new(TYPE => 'STRING', SOURCE => $content_p, H +ASH => $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 => \$c +ontent_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); ###----------------------------------------------------------------### ### 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_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; if ($out_ct ne $out_tt) { debug $out_ct, $out_tt; die "CGI::Ex::Template 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"; } ###----------------------------------------------------------------### 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); }, 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 => "fo +o.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 => "fo +o.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; }, }; my %mem_tests = map {($_ => $tests->{$_})} qw(TT_mem TTX_mem CET_mem H +T_mem HTE_mem); my %cpl_tests = map {($_ => $tests->{$_})} qw(TT_compile TTX_compile C +ET_compile HT_compile); my %str_tests = map {($_ => $tests->{$_})} qw(TT_str TTX_str CET_str H +T_str HTE_str TextTemplate); 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); ###----------------------------------------------------------------###


That script output the output below on my machine (I've removed some of the timethese output to shorten what is presented). Remember that the template is used for the test is trivially short - but it does give a good approximation of "heavy" vs "light". The following abreviations are used:
TT - Template Toolkit with non-XS stash TTX - Template Toolkit with XS stash HTE - HTML::Template::Expr HT - HTML::Template CET - CGI::Ex::Template _str - Means the module was passed a string containing the templat +e. _compile - Means the template was cached on the file system. _mem - Means the template was cached in memory. ---------------------------------------------------------------------- +-- From a string or scalarref tests Rate TT_str TTX_str HTE_str HT_str CET_str Te +xtTemplate TT_str 273/s -- -8% -72% -80% -82% + -83% TTX_str 296/s 9% -- -69% -78% -80% + -82% HTE_str 961/s 252% 224% -- -29% -36% + -40% HT_str 1346/s 393% 354% 40% -- -10% + -16% CET_str 1499/s 449% 406% 56% 11% -- + -7% TextTemplate 1605/s 488% 442% 67% 19% 7% + -- ---------------------------------------------------------------------- +-- Compiled and cached on the file system tests Rate TT_compile TTX_compile HT_compile CET_compile TT_compile 694/s -- -11% -62% -71% TTX_compile 775/s 12% -- -57% -67% HT_compile 1817/s 162% 134% -- -23% CET_compile 2353/s 239% 204% 30% -- ---------------------------------------------------------------------- +-- Cached in memory tests Rate HTE_mem TT_mem HT_mem TTX_mem CET_mem HTE_mem 1374/s -- -37% -41% -57% -60% TT_mem 2167/s 58% -- -7% -31% -38% HT_mem 2340/s 70% 8% -- -26% -33% TTX_mem 3163/s 130% 46% 35% -- -9% CET_mem 3474/s 153% 60% 48% 10% -- ---------------------------------------------------------------------- +-- All variants together Rate TT_str TTX_str TT_compile TTX_compile HTE_str HT_s +tr HTE_mem CET_str TextTemplate HT_compile TT_mem CET_compile HT_mem +TTX_mem CET_mem TT_str 285/s -- -0% -59% -64% -68% -7 +9% -79% -80% -83% -84% -87% -88% -89% + -90% -92% TTX_str 286/s 0% -- -59% -63% -68% -7 +9% -79% -80% -83% -84% -87% -88% -88% + -90% -92% TT_compile 692/s 143% 142% -- -11% -23% -4 +9% -49% -51% -59% -62% -68% -70% -72% + -76% -81% TTX_compile 782/s 175% 174% 13% -- -13% -4 +2% -43% -45% -54% -57% -64% -67% -68% + -73% -78% HTE_str 903/s 217% 216% 30% 15% -- -3 +3% -34% -36% -47% -50% -59% -62% -64% + -69% -75% HT_str 1346/s 373% 371% 94% 72% 49% +-- -1% -5% -21% -26% -38% -43% -46% + -54% -63% HTE_mem 1360/s 378% 376% 96% 74% 51% +1% -- -3% -20% -25% -38% -42% -45% + -54% -62% CET_str 1409/s 395% 393% 104% 80% 56% +5% 4% -- -17% -22% -35% -40% -43% + -52% -61% TextTemplate 1697/s 496% 494% 145% 117% 88% 2 +6% 25% 20% -- -7% -22% -28% -32% + -42% -53% HT_compile 1816/s 538% 536% 162% 132% 101% 3 +5% 34% 29% 7% -- -17% -23% -27% + -38% -50% TT_mem 2177/s 665% 662% 214% 178% 141% 6 +2% 60% 54% 28% 20% -- -7% -12% + -26% -40% CET_compile 2346/s 724% 721% 239% 200% 160% 7 +4% 72% 66% 38% 29% 8% -- -5% + -20% -35% HT_mem 2477/s 770% 767% 258% 217% 174% 8 +4% 82% 76% 46% 36% 14% 6% -- + -16% -32% TTX_mem 2943/s 934% 930% 325% 276% 226% 11 +9% 116% 109% 73% 62% 35% 25% 19% + -- -19% CET_mem 3617/s 1171% 1166% 423% 363% 301% 16 +9% 166% 157% 113% 99% 66% 54% 46% + 23% --


So using Template Toolkit with the XS stash in a mod_perl environment would offer the best performance (not counting CGI::Ex::Template in a cached environment). I do agree that using TT in a CGI environment without any caching may be a bit heavy (I use CGI::Ex::Template in those cases). This has been a long answer to a short comment about TT being too heavy. Apache is heavy too - but it gets the job done.

Personally I use whatever templating system the previous coder used when I go in and maintain code. But on all new work I use CGI::Ex::Template because it gives me the power of TT, but is even faster and lighter.

my @a=qw(random brilliant braindead); print $a[rand(@a)];

Replies are listed 'Best First'.
Re^3: I need to template this
by samtregar (Abbot) on Apr 20, 2007 at 16:46 UTC
    Dude, where's HTML::Template::JIT? I guess you didn't want to blow your premise by including the fastest templating system known to man. (I kid - honestly there's a good reason no one uses it, and that's because templating is almost never the bottleneck!)

    -sam

      I remember reading about H::T::JIT - I just forgot where to look.

      I really think that the way to go is to have all of the various template languages compile to an opcode tree - a common opcode tree - and then make a common runtime engine that is blazingly fast - for all of them. Maybe once parrot and perl6 are done we can just compile the templates to parrot bytecode with optional JIT (if that ever becomes mainstream).

      Really though - I don't know of anybody using H::T - but I know lots of people using H::T::Expr. There wouldn't happen to be a H::T::Expr::JIT would there. I don't think so as there isn't even a file_cache version of H::T::Expr.

      I think the H::T::JIT must be a pretty dandy piece of work. I will add it to the tests - I'm curious now.

      my @a=qw(random brilliant braindead); print $a[rand(@a)];
        Would've had results sooner - but I had to run an errand.

        I added HTML::Template::JIT to the tests. I also added CGI::Ex::Template::XS which I wrote last August - I haven't released it yet because there are two memory leaks in some obscure TT filter operations - and because I haven't seen that much interest. The results are below - I've tried to compact the thing down.

        The abreviations are as before but with the additions of CTX - CGI::Ex::Template::XS HTJ - HTML::Template::JIT TT_str 313/s -- -5% -60% -64% -69% -78% -80% -80% -83% -8 +4% -84% -87% -87% -88% -91% -91% -92% -95% -95% TTX_str 329/s 5% -- -58% -62% -67% -77% -79% -79% -82% -8 +3% -83% -86% -87% -88% -90% -91% -91% -95% -95% TT_compile 778/s 148% 136% -- -11% -23% -45% -50% -50% -57% -6 +0% -60% -67% -69% -71% -77% -78% -80% -87% -89% TTX_compile 872/s 178% 165% 12% -- -14% -39% -44% -44% -51% -5 +5% -55% -63% -65% -68% -74% -76% -77% -86% -87% HTE_str 1009/s 222% 207% 30% 16% -- -29% -35% -35% -44% -4 +8% -48% -57% -60% -63% -70% -72% -74% -84% -85% HT_str 1420/s 354% 332% 83% 63% 41% -- -9% -9% -21% -2 +6% -27% -39% -43% -47% -58% -61% -63% -77% -80% HTE_mem 1556/s 397% 373% 100% 78% 54% 10% -- -0% -13% -1 +9% -20% -34% -38% -42% -54% -57% -59% -75% -78% CET_str 1562/s 399% 375% 101% 79% 55% 10% 0% -- -13% -1 +9% -19% -33% -37% -42% -54% -57% -59% -75% -77% TextTemplate 1790/s 472% 444% 130% 105% 77% 26% 15% 15% -- - +7% -8% -23% -28% -34% -47% -50% -53% -71% -74% CTX_str 1930/s 516% 487% 148% 121% 91% 36% 24% 24% 8% +-- -0% -17% -23% -28% -43% -47% -50% -69% -72% HT_compile 1938/s 519% 489% 149% 122% 92% 37% 25% 24% 8% +0% -- -17% -22% -28% -43% -46% -49% -69% -72% TT_mem 2339/s 647% 611% 201% 168% 132% 65% 50% 50% 31% 2 +1% 21% -- -6% -13% -31% -35% -39% -62% -66% CET_compile 2497/s 698% 659% 221% 186% 148% 76% 61% 60% 40% 2 +9% 29% 7% -- -7% -26% -31% -35% -60% -64% HT_mem 2696/s 761% 719% 247% 209% 167% 90% 73% 73% 51% 4 +0% 39% 15% 8% -- -20% -25% -30% -56% -61% TTX_mem 3377/s 979% 927% 334% 287% 235% 138% 117% 116% 89% 7 +5% 74% 44% 35% 25% -- -7% -12% -45% -51% CTX_compile 3612/s 1054% 998% 364% 314% 258% 154% 132% 131% 102% 8 +7% 86% 54% 45% 34% 7% -- -6% -42% -48% CET_mem 3836/s 1125% 1066% 393% 340% 280% 170% 147% 146% 114% 9 +9% 98% 64% 54% 42% 14% 6% -- -38% -45% HTJ_compile 6193/s 1878% 1782% 696% 610% 514% 336% 298% 296% 246% 22 +1% 219% 165% 148% 130% 83% 71% 61% -- -11% CTX_mem 6934/s 2115% 2007% 791% 695% 587% 388% 346% 344% 287% 25 +9% 258% 196% 178% 157% 105% 92% 81% 12% --


        So - H::T::JIT is very very very fast. Faster than just about anything else. It also is nice because it doesn't have to stay resident in memory to give a speed boost. You just compile the template to c and run it from scratch each time (unlike the CTX_mem test that was faster - but had to keep the compiled template in memory such as you would do with mod_perl). The downside to H::T::JIT is that there is no expression support (I hate to fan the expression/no-expression fire - but most people I work or talk with use expressions in their templates - but we're talking a couple dozen people at most).

        Someday - I'd really love to have a module that compiled the templates down to Parrot bytecode - once - and then executed them - or even better JITed them and then executed them. I think Parrot based templates would fly because you could avoid the costly process of jumping from Perl to C and back - you'd stay in the Parrot interpreter.

        If we don't do (or even if we did do) the parrot version, then at minimum I'd love to see a "common compiled template" format that was able to suit all of the various template engines. I think my parsed optree comes pretty close to being suitable - but it certainly isn't commonly used. With a little more massaging, my current format could be stored language agnostic as JSON or some other low level type. This would make life easier for the Jemplate guys - and it would also help out the Python guys who are currently trying to make version of TT in python. We could all share a common compiled template between the languages and various template syntaxes and create "the one true executor" in each language - possibly with an extensible c based library used by each language. OK - now I'm rambling.

        Not that you'd be interested (but if you are) - it would be relatively easy to create HTML::Template::TT based on HTML::Template and the parse_expr and play_expr methods of CGI::Ex::Template that would offer TT style expression support in HTML::Templates. It could even go a little farther and compile the HTML::Template to the opcode tree that I use - at which point it could be cached as a CGI::Ex::Template pre-compiled template. I have though about this recently as I've been maintaining some HTML::Template::Expr templates. Just a thought.

        my @a=qw(random brilliant braindead); print $a[rand(@a)];