-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathREADME
913 lines (738 loc) · 39.6 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
NAME
Template::Benchmark - Pluggable benchmarker to cross-compare template
systems.
VERSION
version 1.09_02
SYNOPSIS
use Template::Benchmark;
my $bench = Template::Benchmark->new(
duration => 5,
template_repeats => 1,
array_loop => 1,
shared_memory_cache => 0,
);
my $result = $bench->benchmark();
if( $result->{ result } eq 'SUCCESS' )
{
...
}
DESCRIPTION
Template::Benchmark provides a pluggable framework for cross-comparing
performance of various template engines across a range of supported
features for each, grouped by caching methodology.
If that's a bit of a mouthful... have you ever wanted to find out the
relative performance of template modules that support expression parsing
when running with a shared memory cache? Do you even know which ones
*allow* you to do that? This module lets you find that sort of thing
out.
As of current writing, there are plugins to let you compare the
performance and features of 21 different perl template engines in a
total of 75 different configurations.
If you're just after results, then you should probably start with the
benchmark_template_engines script first, it provides a commandline UI
onto Template::Benchmark and gives you human-readable reports as a reply
rather than a raw hashref, it also supports JSON output if you want to
dump the report somewhere in a machine-readable format.
If you have no template engines already installed, or you want to
benchmark everything supported, I suggest you also look at the
Task::Template::Benchmark distribution which installs all the optional
requirements for Template::Benchmark.
IMPORTANT CONCEPTS AND TERMINOLOGY
Template Engines
Template::Benchmark is built around a plugin structure using
Module::Pluggable, it will look under "Template::Benchmark::Engines::*"
for *template engine* plugins.
Each of these plugins provides an interface to a different *template
engine* such as Template::Toolkit, HTML::Template, Template::Sandbox and
so on.
Cache Types
*Cache types* determine the source of the template and the caching
mechanic applied, currently there are the following *cache types*:
*uncached_string*, *uncached_disk*, *disk_cache*, *shared_memory_cache*,
*memory_cache* and *instance_reuse*.
For a full list, and for an explanation of what they represent, consult
the "Cache Types" in Template::Benchmark::Engine documentation.
Template Features
*Template features* are a list of features supported by the various
*template engines*, not all are implemented by all *engines* although
there's a core set of *features* supported by all *engines*.
*Features* can be things like *literal_text*, *records_loop*,
*scalar_variable*, *variable_expression* and so forth.
For a full list, and for an explanation of what they represent, consult
the "Template Features" in Template::Benchmark::Engine documentation.
Benchmark Functions
Each *template engine* plugin provides the means to produce a *benchmark
function* for each *cache type*.
The *benchmark function* is an anonymous sub that is expected to be
passed the template, and two hashrefs of template variables, and is
expected to return the output of the processed template.
These are the functions that will be benchmarked, and generally consist
(depending on the *template engine*) of a call to the template
constructor and template processing functions.
Each plugin can return several *benchmark functions* for a given *cache
type*, so each is given a tag to use as a name and a description for
display, this allows plugins like
Template::Benchmark::Engines::TemplateToolkit to contain benchmarks for
Template::Toolkit, Template::Toolkit running with Template::Stash::XS,
and various other options.
Each of these will run as an independent benchmark even though they're
provided by the same plugin.
Supported or Unsupported?
Throughout this document are references to whether a *template feature*
or *cache type* is supported or unsupported in the *template engine*.
But what constitutes "unsupported"?
It doesn't neccessarily mean that it's *impossible* to perform that task
with the given *template engine*, but generally if it requires some
significant chunk of DIY code or boilerplate or subclassing by the
developer using the *template engine*, it should be considered to be
*unsupported* by the *template engine* itself.
This of course is a subjective judgement, but a general rule of thumb is
that if you can tell the *template engine* to do it, it's supported; and
if the *template engine* allows *you* to do it, it's *unsupported*, even
though it's *possible*.
HOW Template::Benchmark WORKS
Construction
When a new Template::Benchmark object is constructed, it attempts to
load all *template engine* plugins it finds.
It then asks each plugin for a snippet of template to implement each
*template feature* requested. If a plugin provides no snippet then it is
assumed that that *feature* is unsupported by that *engine*.
Each snippet is then combined into a benchmark template for that
specific *template engine* and written to a temporary directory, at the
same time a cache directory is set up for that *engine*. These temporary
directories are cleaned up in the "DESTROY()" of the benchmark instance,
usually when you let it go out of scope.
Finally, each *engine* is asked to provide a list of *benchmark
functions* for each *cache type* along with a name and description
explaining what the *benchmark function* is doing.
At this point the Template::Benchmark constructor exits, and you're
ready to run the benchmarks.
Running the benchmarks
When the calling program is ready to run the benchmarks it calls
"$bench->benchmark()" and then twiddles its thumbs, probably for a long
time.
While this twiddling is going on, Template::Benchmark is busy running
each of the *benchmark functions* a single time.
The outputs of this initial run are compared and if there are any
mismatches then the "$bench->benchmark()" function exits early with a
result structure indicating the errors as compared to a reference copy
produced by the reference plugin engine.
An important side-effect of this initial run is that the cache for each
*benchmark function* becomes populated, so that the cached *cache types*
truly reflect only cached performance and not the cost of an initial
cache miss.
If all the outputs match then the *benchmark functions* for each *cache
type* are handed off to the Benchmark module for benchmarking.
The results of the benchmarks are bundled together and placed into the
results structure that is returned from "$bench->benchmark()".
OPTIONS
New <Template:Benchmark> objects can be created with the constructor
"Template::Benchmark->new( %options )", using any (or none) of the
options below.
uncached_string => *0* | *1* (default 1)
uncached_disk => *0* | *1* (default 1)
disk_cache => *0* | *1* (default 1)
shared_memory_cache => *0* | *1* (default 1)
memory_cache => *0* | *1* (default 1)
instance_reuse => *0* | *1* (default 1)
Each of these options determines which *cache types* are enabled (if
set to a true value) or disabled (if set to a false value). At least
one of them must be set to a true value for any benchmarks to be
run.
literal_text => *0* | *1* | *$n* (default 1)
scalar_variable => *0* | *1* | *$n* (default 1)
hash_variable_value => *0* | *1* | *$n* (default 0)
array_variable_value => *0* | *1* | *$n* (default 0)
deep_data_structure_value => *0* | *1* | *$n* (default 0)
array_loop_value => *0* | *1* | *$n* (default 0)
hash_loop_value => *0* | *1* | *$n* (default 0)
records_loop_value => *0* | *1* | *$n* (default 1)
array_loop_template => *0* | *1* | *$n* (default 0)
hash_loop_template => *0* | *1* | *$n* (default 0)
records_loop_template => *0* | *1* | *$n* (default 1)
constant_if_literal => *0* | *1* | *$n* (default 0)
variable_if_literal => *0* | *1* | *$n* (default 1)
constant_if_else_literal => *0* | *1* | *$n* (default 0)
variable_if_else_literal => *0* | *1* | *$n* (default 1)
constant_if_template => *0* | *1* | *$n* (default 0)
variable_if_template => *0* | *1* | *$n* (default 1)
constant_if_else_template => *0* | *1* | *$n* (default 0)
variable_if_else_template => *0* | *1* | *$n* (default 1)
constant_expression => *0* | *1* | *$n* (default 0)
variable_expression => *0* | *1* | *$n* (default 0)
complex_variable_expression => *0* | *1* | *$n* (default 0)
constant_function => *0* | *1* | *$n* (default 0)
variable_function => *0* | *1* | *$n* (default 0)
Each of these options sets the corresponding *template feature* on
or off. At least one of these must be true for any benchmarks to
run.
If any option is set to a positive integer greater than 1, it will
be used as a repeats value for that specific feature, indicating
that the feature should appear in the benchmark template that number
of times.
This stacks with any "template_repeats" constructor option, for
example if you supply "scalar_variable => 2" and "template_repeats
=> 30", you will have 60 "scalar_variable" sections in the benchmark
template.
Support for per-feature repeats values was added in 1.04.
features_from => *$plugin* (default none)
If set, then the list of *template features* will be drawn from
those supported by the given plugin.
If this option is set, any *template features* you supply as options
will be ignored and overwritten, and the plugin named will also be
benchmarked even if you attempted to exclude it with the
"skip_plugin" or "only_plugin" options.
You can supply multiple plugins to "features_from", either by
passing several "features_from" attributes, or by passing an
arrayref of plugin names, or a hashref of plugin names with
true/false values to toggle them on or off. The set of features
chosen will then be the largest common subset of features supported
by those engines.
# This sets the features to benchmark to all those supported by
# Template::Benchmark::Engines::TemplateSandbox
$bench = Template::Benchmark->new(
features_from => 'TemplateSandbox',
);
# This sets the features to benchmark to all those supported by BOTH
# Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
features_from => 'MojoTemplate',
features_from => 'HTMLTemplateCompiled',
);
# This sets the features to benchmark to all those supported by BOTH
# Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
features_from => {
MojoTemplate => 1,
HTMLTemplateCompiled => 1,
TemplateSandbox => 0,
},
);
Support for multiple "features_from" values was added in 1.09.
cache_types_from => *$plugin* (default none)
If set, then the list of *cache types* will be drawn from those
supported by the given plugin.
If this option is set, any *cache types* you supply as options will
be ignored and overwritten, and the plugin named will also be
benchmarked even if you attempted to exclude it with the
"skip_plugin" or "only_plugin" options.
You can supply multiple plugins to "cache_types_from", either by
passing several "cache_types_from" attributes, or by passing an
arrayref of plugin names, or a hashref of plugin names with
true/false values to toggle them on or off. The set of cache types
chosen will then be the superset of cache types supported by those
engines.
# This sets the cache types to benchmark to all those supported by
# Template::Benchmark::Engines::TemplateSandbox
$bench = Template::Benchmark->new(
cache_types_from => 'TemplateSandbox',
);
# This sets the cache types to benchmark to all those supported by EITHER of
# Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
cache_types_from => 'MojoTemplate',
cache_types_from => 'HTMLTemplateCompiled',
);
# This sets the features to benchmark to all those supported by EITHER of
# Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
cache_types_from => {
MojoTemplate => 1,
HTMLTemplateCompiled => 1,
TemplateSandbox => 0,
},
);
Support for multiple "cache_types_from" values was added in 1.09.
template_repeats => *$number* (default 30)
After the template is constructed from the various feature snippets
it gets repeated a number of times to make it longer, this option
controls how many times the basic template gets repeated to form the
final template.
The default of 30 is chosen to provide some form of approximation of
the workload in a "normal" web page. Given that "how long is a web
page?" has much the same answer as "how long is a piece of string?"
you will probably want to tweak the number of repeats to suit your
own needs.
duration => *$seconds* (default 10)
This option determines how many CPU seconds should be spent running
each *benchmark function*, this is passed along to Benchmark as a
negative duration, so read the Benchmark documentation if you want
the gory details.
The larger the number the less statistical variance you'll get, the
less likely you are to have temporary blips of the test machine's
I/O or CPU skewing the results, the downside is that your benchmarks
will take corresspondingly longer to run.
The default of 10 seconds seems to give pretty consistent results
for me within +/-1% on a very lightly loaded linux machine.
dataset => *$dataset_name* (default 'original')
dataset => { hash1 => { *variables* }, hash2 => { *variables* } }
Sets the dataset of *template variables* to use for the benchmark
function, either as the name of one of the presupplied datasets or
as a hashref to a custom dataset.
Currently the only presupplied dataset is 'original'.
If supplying a custom dataset the hashref should consist of only two
keys, named 'hash1' and 'hash2' the values of which will be passed
as the two hashes of *template variables* to each *benchmark
function*.
Please see the section "CUSTOM DATASETS" for more details on
supplying your own datasets.
The "dataset" option was added in 1.04.
style => *$string* (default 'none')
This option is passed straight through as the "style" argument to
Benchmark. By default it is 'none' so that no output is printed by
Benchmark, this also means that you can't see any results until all
the benchmarks are done. If you set it to 'auto' then you'll see the
benchmark results as they happen, but Template::Benchmark will have
no control over the generated output.
Might be handy for debugging or if you're impatient and don't want
pretty reports.
See the Benchmark documentation for valid values for this setting.
keep_tmp_dirs => *0* | *1* (default 0)
If set to a true value then the temporary directories created for
template files and caches will not be deleted when the
Template::Benchmark instance is destroyed. Instead, at the point
when they would have been deleted, their location will be printed.
This allows you to inspect the directory contents to see the
generated templates and caches and so forth.
Because the location is printed, and at an unpredictable time, it
may mess up your program output, so this option is probably only
useful while debugging.
skip_output_compare => *0* | *1* (default 0)
If enabled, this option will skip the sanity check that compares the
output of the different template engines to ensure that they're
producing the same output.
This is useful as a workaround if the sanity check is producing a
mismatch error that you deem to be unimportant.
It is strongly recommended that you never use this option without
first manually checking the mismatched outputs to be certain that
they are in fact unimportant.
If you find yourself needing to use this option as a workaround,
please file a bug report at
<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Benchmark>.
The "skip_output_compare" option was added in 1.05.
only_plugin => *$plugin* (default none)
skip_plugin => *$plugin* (default none)
If either of these two options are set they are used as a
'whitelist' and 'blacklist' of what *template engine* plugins to
use.
Each can be supplied multiple times to build the whitelist or
blacklist, and expect the leaf module name, or you can supply an
arrayref of names, or a hashref of names with true/false values to
toggle them on or off.
# This runs only Template::Benchmark::Engines::TemplateSandbox
$bench = Template::Benchmark->new(
only_plugin => 'TemplateSandbox',
);
# This skips Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
skip_plugin => 'MojoTemplate',
skip_plugin => 'HTMLTemplateCompiled',
);
# This runs only Template::Benchmark::Engines::MojoTemplate and
# Template::Benchmark::Engines::HTMLTemplateCompiled
$bench = Template::Benchmark->new(
only_plugin => {
MojoTemplate => 1,
HTMLTemplateCompiled => 1,
TemplateSandbox => 0,
},
);
PUBLIC METHODS
*$benchmark* = Template::Benchmark->new( *%options* )
This is the constructor for Template::Benchmark, it will return a
newly constructed benchmark object, or throw an exception explaining
why it couldn't.
The options you can pass in are covered in the "OPTIONS" section
above.
*$result* = $benchmark->benchmark()
Run the benchmarks as set up by the constructor. You can run
"$benchmark->benchmark()" multiple times if you wish to reuse the
same benchmark options.
The structure of the $result hashref is covered in "BENCHMARK
RESULTS" below.
*%defaults* = Template::Benchmark->default_options()
Returns a hash of the valid options to the constructor and their
default values. This can be used to keep external programs
up-to-date with what options are available in case new ones are
added or the defaults are changed. This is what
benchmark_template_engines does in fact.
*@cache_types* = Template::Benchmark->valid_cache_types()
Returns a list of the valid *cache types*. This can be used to keep
external programs up-to-date with what *cache types* are available
in case new ones are added. benchmark_template_engines does just
that.
*@features* = Template::Benchmark->valid_features()
Returns a list of the valid *template features*. This can be used to
keep external programs up-to-date with what *template features* are
available in case new ones are added. This is how
benchmark_template_engines gets at this info too.
$errors = $benchmark->engine_errors()
Returns a hashref of *engine* plugin to an arrayref of error
messages encountered while trying to enable to given plugin for a
benchmark.
This may be errors in loading the module or a list of *template
features* the *engine* didn't support.
$benchmark->engine_error( *$engine*, *$error_message* )
Pushes *$error_message* onto the list of error messages for the
engine plugin *$engine*.
*$number* = $benchmark->number_of_benchmarks()
Returns a count of how many *benchmark functions* will be run.
*$seconds* = $benchmark->estimate_benchmark_duration()
Return an estimate, in seconds, of how long it will take to run all
the benchmarks.
This estimate currently isn't a very good one, it's basically the
duration multiplied by the number of *benchmark functions*, and
doesn't count factors like the overhead of running the benchmarks,
or the fact that the duration is a minimum duration, or the initial
run of the *benchmark functions* to build the cache and compare
outputs.
It still gives a good lower-bound for how long the benchmark will
run, and maybe I'll improve it in future releases.
*@engines* = $benchmark->engines()
Returns a list of all *template engine plugins* that were
successfully loaded.
Note that this does not mean that all those *template engines*
support all requested *template features*, it merely means there
wasn't a problem loading their module.
*@features* = $benchmark->features()
Returns a list of all *template features* that were enabled during
construction of the Template::Benchmark object.
BENCHMARK RESULTS
The "$benchmark->benchmark()" method returns a results hashref, this
section documents the structure of that hashref.
Firstly, all results returned have a "result" key indicating the type of
result, this defines the format of the rest of the hashref and whether
the benchmark run was a success or why it failed.
"SUCCESS"
This indicates that the benchmark run completed successfully, there
will be the following additional information:
{
result => 'SUCCESS',
start_time => 1265738228,
title => 'Template Benchmark @Tue Feb 9 17:57:08 2010',
descriptions =>
{
'HT' =>
'HTML::Template (2.9)',
'TS_CF' =>
'Template::Sandbox (1.02) with Cache::CacheFactory (1.09) caching',
},
reference =>
{
type => 'uncached_string',
tag => 'TS',
output => template output,
},
benchmarks =>
[
{
type => 'uncached_string',
timings => Benchmark::timethese() results,
comparison => Benchmark::cmpthese() results,
},
{
type => 'memory_cache',
timings => Benchmark::timethese() results,
comparison => Benchmark::cmpthese() results,
},
...
],
}
"NO BENCHMARKS TO RUN"
{
result => 'NO BENCHMARKS TO RUN',
}
"MISMATCHED TEMPLATE OUTPUT"
{
result => 'MISMATCHED TEMPLATE OUTPUT',
reference =>
{
type => 'uncached_string',
tag => 'TS',
output => template output,
},
failures =>
[
{
type => 'disk_cache',
tag => 'TT',
output => template output,
},
...
],
}
WRITING YOUR OWN TEMPLATE ENGINE PLUGINS
All *template engine* plugins reside in the
"Template::Benchmark::Engines" namespace and inherit the
Template::Benchmark::Engine class.
See the Template::Benchmark::Engine documentation for details on writing
your own plugins.
CUSTOM DATASETS
Starting with version 1.04, Template::Benchmark has allowed you to
supply your own data to use as *template variables* within the
benchmarks.
This is done by supplying a hashref to the "dataset" constructor option,
with two keys, 'hash1' and 'hash2' which in-turn have hashref values
which are to be used as the hashrefs of *template variables* supplied to
the *benchmark functions*:
$bench = Template::Benchmark->new(
dataset => {
hash1 => {
scalar_variable => 'I is a scalar, yarr!',
hash_variable => {
'hash_value_key' =>
'I spy with my little eye, something beginning with H.',
},
array_variable => [ qw/I have an imagination honest/ ],
this => { is => { a => { very => { deep => { hash => {
structure => "My god, it be full of hashes.",
} } } } } },
template_if_true => 'True dat',
template_if_false => 'Nay, Mister Wilks',
},
hash2 => {
array_loop =>
[ qw/five four three two one coming ready or not/ ],
hash_loop => {
aaa => 'first',
bbb => 'second',
ccc => 'third',
ddd => 'fourth',
eee => 'fifth',
},
records_loop => [
{ name => 'Joe Bloggs', age => 16, },
{ name => 'Fred Bloggs', age => 23, },
{ name => 'Nigel Bloggs', age => 43, },
{ name => 'Tarquin Bloggs', age => 143, },
{ name => 'Geoffrey Bloggs', age => 13, },
],
variable_if => 1,
variable_if_else => 0,
variable_expression_a => 20,
variable_expression_b => 10,
variable_function_arg => 'Hi there',
},
},
);
There are no constraints on what *template variable* belongs in "hash1"
and "hash2", just so long as it occurs once and only once in both.
"scalar_variable"
This value gets interpolated into the template as part of the
"scalar_variable" feature, and can be any string value.
"hash_variable"
Used by the "hash_variable_value" feature, this value must be a
hashref with key 'hash_value_key' pointing at a string value to be
interpolated into the template.
"array_variable"
Used by the "array_variable_value" feature, this value must be an
arrayref consisting of at least 3 elements, the third of which will
be interpolated into the template.
"this"
The base of a deep data-structure for use in the
"deep_data_structure_value" feature, this should be a nested series
of hashrefs keyed in turn by 'this', 'is', 'a', 'very', 'deep',
'hash', 'structure', with the final 'structure' key referring to a
string value to interpolate into the template.
"array_loop"
Used within the "array_loop_value" and "array_loop_template"
features, this value should be an arrayref of any number of strings,
all of which will be looped across and included in the template
output.
"hash_loop"
Used within the "hash_loop_value" and "hash_loop_template" features,
this value should be a hashref of any number of string keys mapping
to string values, all of which will be looped across and included in
the template output.
"records_loop"
Used within the "records_loop_value" and "records_loop_template"
features, this value should be an arrayref of any number of hashrefs
"records", each consisting of a 'name' key with a string value, and
an 'age' key with integer value. Each record will be iterated across
and written to the template output.
"variable_if"
"variable_if_else"
These two keys are used by the "variable_if_*" and
"variable_if_else_*" features and should be set to a true or false
integer which will determine which branch of the if-construct will
be taken.
"template_if_true"
"template_if_false"
These two keys should have distinct string values that will be
interpolated into the template as the content in the *_if_template
and *_if_else_template features, depending on which branch is taken.
"variable_expression_a"
"variable_expression_b"
These two values are used in the "variable_expression" and
"complex_variable_expression" features, and should be integer
values.
For most consistent results they should probably be chosen with care
to keep the two features to integer mathematics to avoid rounding or
floating-point inconsistencies between different template engines,
the default values of 20 and 10 respectively do this.
See the Template::Benchmark::Engine documentation for further
details.
"variable_function_arg"
Used by the "variable_function" feature, this value should be a
string of at least 6 characters length, the 4th through 6th of which
will be used in the template output.
UNDERSTANDING THE RESULTS
This section aims to give you a few pointers when analyzing the results
of a benchmark run, some points are obvious, some less so, and most need
to be applied with some degree of intelligence to know when they're
applicable or not.
Hopefully they'll prove useful.
If you're wondering what all the numbers mean, the documentation for
Benchmark will probably be more helpful.
memory_cache vs instance_reuse
Comparing the *memory_cache* and *instance_reuse* times for an
*engine* should generally give you some idea of the overhead of the
caching system used by the *engine* - if the times are close then
they're using a good caching system, if the times are wildly
divergent then you might want to implement your own cache instead.
uncached_string vs instance_reuse or memory_cache
Comparing the *uncached_string* vs the *instance_reuse* or the
*memory_cache* (*instance_reuse* is better if you can) times for an
*engine* should give you an indication of how costly the parse and
compile phase for a *template engine* is.
uncached_string or uncached_disk represents a cache miss
The *uncached_string* or *uncached_disk* benchmark represents a
cache miss, so comparing it to the cache system you intend to use
will give you an idea of how much you'll hurt whenever a cache miss
occurs.
If you know how likely a cache miss is to happen, you can combine
the results of the two benchmarks proportionally to get a better
estimate of performance, and maybe compare that between different
engines.
Estimating cache misses is a tricky art though, and can be mitigated
by a number of measures, or complicated by miss stampedes and so
forth, so don't put too much weight on it either.
Increasing repeats emphasises template performance
Increasing the length of the template by increasing the
"template_repeats" option *usually* places emphasis on the ability
of the *template engine* to process the template vs the overhead of
reading the template, fetching it from the cache, placing the
variables into the template namespace and so forth.
For the most part those overheads are fixed cost regardless of
length of the template (fetching from disk or cache will have a,
usually small, linear component), whereas actually executing the
template will have a linear cost based on the repeats.
This means that for small values of repeats you're spending
proportionally more time on overheads, and for large values of
repeats you're spending more time on running the template.
If a *template engine* has higher-than-average overheads, it will be
favoured in the results (ie, it will rank higher than otherwise) if
you run with a high "template_repeats" value, and will be hurt in
the results if you run with a low "template_repeats" value.
Inverting that conclusion, if an *engine* moves up in the results
when you run with long repeats, or moves down in the results if you
run with short repeats, it follows that the *engine* probably has
high overheads in I/O, instantiation, variable import or somewhere.
deep_data_structure_value and complex_variable_expression are stress
tests
Both the "deep_data_structure_value" and
"complex_variable_expression" *template features* are designed to be
*stress test* versions of a more basic feature.
By comparing "deep_data_structure_value" vs "hash_variable_value"
you should be able to glean an indication of how well the *template
engine* performs at navigating its way through its variable stash
(to borrow Template::Toolkit terminology).
If an *engine* gains ranks moving from "hash_variable_value" to
"deep_data_structure_value" then you know it has a
more-efficient-than-average implementation of its stash, and if it
loses ranks then you know it has a less-efficient-than-average
implementation.
Similarly, by comparing "complex_variable_expression" and
"variable_expression" you can draw conclusions about the *template
engine's* expression execution speed.
constant vs variable features
Several *template features* have "constant" and "variable" versions,
these indicate a version that is designed to be easily optimizable
(the "constant" one) and a version that cannot be optimized (the
"variable" one).
By comparing timings for the two versions, you can get a feel for
whether (and how much) constant-folding optimization is done by a
*template engine*.
Whether this is of interest to you depends entirely on how you
construct and design your templates, but generally speaking, the
larger and more modular your template structure is, the more likely
you are to have bits of constant values "inherited" from parent
templates (or config files) that could be optimized in this manner.
This is one of those cases where only you can judge whether it is
applicable to your situation or not, Template::Benchmark merely
provides the information so you can make that judgement.
duration only effects accuracy
The benchmarks are carefully designed so that any one-off costs from
setting up the benchmark are not included in the benchmark results
itself.
This means that there *should* be no change in the results from
increasing or decreasing the benchmark duration, except to reduce
the size of the error resulting from background load on the machine.
If a *template engine* gets consistently better (or worse) results
as duration is changed, while other *template engines* are unchanged
(give or take statistical error), it indicates that something is
wrong with either the *template engine*, the plugin or something
else - either way the results of the benchmark should be regarded as
suspect until the cause has been isolated.
KNOWN ISSUES AND BUGS
Test suite is non-existent
The current test-suite is laughable and basically only tests
documentation coverage.
Once I figure out what to test and how to do it, this should change,
but at the moment I'm drawing a blank.
Results structure too terse
The results structure could probably do with more information such
as what options were set and what version of benchmark/plugins were
used.
This would be helpful for anything wishing to archive benchmark
results, since it may (will!) influence how comparable results are.
Engine meta-data not retrievable if template engine isn't installed
The *template engine* plugins are designed in a way that requires
their respective *template engine* to be installed for the plugin to
compile successfully: this allows the benchmarked code in the plugin
to be free of cruft testing for module availability, which in turn
gives it a more accurate benchmark.
The downside of this approach is that none of the meta-information
like syntaxes, engine descriptions, syntax type, and "pure-perl"ness
is available unless the *template engine* is available.
This is potentially sucky, but on the other hand it's probably an
indication that the meta-information ought to belong elsewhere,
probably in a different distribution entirely as it's not
specifically to do with benchmarking.
SEE ALSO
Task::Template::Benchmark
BUGS
Please report any bugs or feature requests to "bug-template-benchmark at
rt.cpan.org", or through the web interface at
<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Benchmark>. I
will be notified, and then you'll automatically be notified of progress
on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Template::Benchmark
You can also look for information at:
* RT: CPAN's request tracker
<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Benchmark>
* AnnoCPAN: Annotated CPAN documentation
<http://annocpan.org/dist/Template-Benchmark>
* CPAN Ratings
<http://cpanratings.perl.org/d/Template-Benchmark>
* Search CPAN
<http://search.cpan.org/dist/Template-Benchmark/>
ACKNOWLEDGEMENTS
Thanks to Paul Seamons for creating the the bench_various_templaters.pl
script distributed with Template::Alloy, which was the ultimate
inspiration for this module.
Thanks also for contributions by Goro Fuji and Alex Efros.
AUTHOR
Sam Graham <libtemplate-benchmark-perl BLAHBLAH illusori.co.uk>
COPYRIGHT AND LICENSE
This software is copyright (c) 2010-2011 by Sam Graham
<libtemplate-benchmark-perl BLAHBLAH illusori.co.uk>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.