Home

Awesome

goTemplateBenchmark

comparing the performance of different template engines

full featured template engines

precompilation to Go code

special benchmarks for comparison

transpiling to Go Template

Why?

Just for fun. Go Templates work nice out of the box and should be used for rendering from a security point of view. If you care about performance you should cache the rendered output.

Sometimes there are templates that cannot be reasonably cached. Then you might need a really fast template engine with code generation.

How to run the benchmarks

minimal configuration, but does not make sense, as it would compare the same 2 go versions.

./bench.sh -c go

testing your setup - reduce test runtime and assuming you have those two go binaries on your system.

./bench.sh -t 10ms -c go1.18 -g go1.19

example for a normal benchmark run on my system with the default 3s runtime per template engine

./bench.sh -c go1.18.6 -g go1.19.1

Results dev machine

local desktop: ryzen 3900x

special benchmarks

NameRunsns/opB/opallocations/op
ComplexGoDirectBuffer8,153,42742800
ComplexGoStaticString310,142,2651200
comparing: go1.19.5 to go1.20
name                      old time/op    new time/op    delta
ComplexGoDirectBuffer-24     519ns ± 0%     428ns ± 0%  -17.63%
ComplexGoStaticString-24    11.7ns ± 0%    11.9ns ± 0%   +1.96%

name                      old alloc/op   new alloc/op   delta
ComplexGoDirectBuffer-24     0.00B          0.00B         0.00%
ComplexGoStaticString-24     0.00B          0.00B         0.00%

name                      old allocs/op  new allocs/op  delta
ComplexGoDirectBuffer-24      0.00           0.00         0.00%
ComplexGoStaticString-24      0.00           0.00         0.00%

simple benchmarks

full featured template engines

NameRunsµs/opB/opallocations/op
Ace289,50712.1651,12140
Amber381,0308.53584936
Golang694,0838.19776935
GolangText1,500,3742.3551287
Handlebars266,23813.0623,64878
JetHTML4,429,3360.77100
Mustache859,5524.1771,72330
Pongo2840,6045.4412,07532
Soy1,000,0003.6101,22419
comparing: go1.19.5 to go1.20
name           old time/op    new time/op    delta
Golang-24        8.42µs ± 0%    8.20µs ± 0%  -2.61%
GolangText-24    2.47µs ± 0%    2.35µs ± 0%  -4.81%
Ace-24           12.8µs ± 0%    12.2µs ± 0%  -4.75%
Amber-24         8.57µs ± 0%    8.54µs ± 0%  -0.42%
Mustache-24      4.46µs ± 0%    4.18µs ± 0%  -6.41%
Pongo2-24        5.91µs ± 0%    5.44µs ± 0%  -7.98%
Handlebars-24    13.9µs ± 0%    13.1µs ± 0%  -6.26%
Soy-24           3.67µs ± 0%    3.61µs ± 0%  -1.77%
JetHTML-24        832ns ± 0%     771ns ± 0%  -7.39%

name           old alloc/op   new alloc/op   delta
Golang-24          769B ± 0%      769B ± 0%   0.00%
GolangText-24      128B ± 0%      128B ± 0%   0.00%
Ace-24           1.12kB ± 0%    1.12kB ± 0%   0.00%
Amber-24           849B ± 0%      849B ± 0%   0.00%
Mustache-24      1.72kB ± 0%    1.72kB ± 0%   0.00%
Pongo2-24        2.08kB ± 0%    2.08kB ± 0%   0.00%
Handlebars-24    3.65kB ± 0%    3.65kB ± 0%   0.00%
Soy-24           1.22kB ± 0%    1.22kB ± 0%   0.00%
JetHTML-24        0.00B          0.00B        0.00%

name           old allocs/op  new allocs/op  delta
Golang-24          35.0 ± 0%      35.0 ± 0%   0.00%
GolangText-24      7.00 ± 0%      7.00 ± 0%   0.00%
Ace-24             40.0 ± 0%      40.0 ± 0%   0.00%
Amber-24           36.0 ± 0%      36.0 ± 0%   0.00%
Mustache-24        30.0 ± 0%      30.0 ± 0%   0.00%
Pongo2-24          32.0 ± 0%      32.0 ± 0%   0.00%
Handlebars-24      78.0 ± 0%      78.0 ± 0%   0.00%
Soy-24             19.0 ± 0%      19.0 ± 0%   0.00%
JetHTML-24         0.00           0.00        0.00%

precompilation to Go code

NameRunsµs/opB/opallocations/op
Ego3,272,6161.151858
Ftmpl2,179,8551.61077412
Gorazor4,294,6100.8035125
Hero24,494,6060.12300
Jade40,706,6950.08900
Quicktemplate19,722,9390.18100
comparing: go1.19.5 to go1.20
name              old time/op    new time/op    delta
Ego-24              1.13µs ± 0%    1.15µs ± 0%   +2.22%
Quicktemplate-24     185ns ± 0%     181ns ± 0%   -1.95%
Ftmpl-24            1.46µs ± 0%    1.61µs ± 0%  +10.05%
Gorazor-24           804ns ± 0%     803ns ± 0%   -0.06%
Hero-24              119ns ± 0%     123ns ± 0%   +3.20%
Jade-24             91.5ns ± 0%    88.8ns ± 0%   -2.86%

name              old alloc/op   new alloc/op   delta
Ego-24               85.0B ± 0%     85.0B ± 0%    0.00%
Quicktemplate-24     0.00B          0.00B         0.00%
Ftmpl-24              774B ± 0%      774B ± 0%    0.00%
Gorazor-24            512B ± 0%      512B ± 0%    0.00%
Hero-24              0.00B          0.00B         0.00%
Jade-24              0.00B          0.00B         0.00%

name              old allocs/op  new allocs/op  delta
Ego-24                8.00 ± 0%      8.00 ± 0%    0.00%
Quicktemplate-24      0.00           0.00         0.00%
Ftmpl-24              12.0 ± 0%      12.0 ± 0%    0.00%
Gorazor-24            5.00 ± 0%      5.00 ± 0%    0.00%
Hero-24               0.00           0.00         0.00%
Jade-24               0.00           0.00         0.00%

more complex test with template inheritance (if possible)

full featured template engines

NameRunsµs/opB/opallocations/op
ComplexGolang55,24972.2936,548290
ComplexGolangText115,64031.7312,236107
ComplexJetHTML313,85511.8325345
ComplexMustache134,22626.1567,274156
comparing: go1.19.5 to go1.20
name                  old time/op    new time/op    delta
ComplexGolang-24        72.8µs ± 0%    72.3µs ± 0%  -0.73%
ComplexGolangText-24    31.8µs ± 0%    31.7µs ± 0%  -0.20%
ComplexMustache-24      26.7µs ± 0%    26.2µs ± 0%  -2.07%
ComplexJetHTML-24       11.7µs ± 0%    11.8µs ± 0%  +1.13%

name                  old alloc/op   new alloc/op   delta
ComplexGolang-24        6.64kB ± 0%    6.55kB ± 0%  -1.46%
ComplexGolangText-24    2.24kB ± 0%    2.24kB ± 0%   0.00%
ComplexMustache-24      7.27kB ± 0%    7.27kB ± 0%  +0.01%
ComplexJetHTML-24         534B ± 0%      534B ± 0%   0.00%

name                  old allocs/op  new allocs/op  delta
ComplexGolang-24           290 ± 0%       290 ± 0%   0.00%
ComplexGolangText-24       107 ± 0%       107 ± 0%   0.00%
ComplexMustache-24         156 ± 0%       156 ± 0%   0.00%
ComplexJetHTML-24         5.00 ± 0%      5.00 ± 0%   0.00%

precompilation to Go code

NameRunsµs/opB/opallocations/op
ComplexEgo837,6085.77756831
ComplexFtmpl486,4277.1263,53538
ComplexGorazor720,0135.1933,68824
ComplexHero3,623,1860.95500
ComplexJade5,046,9760.74500
ComplexQuicktemplate3,481,2301.01100
comparing: go1.19.5 to go1.20
name                     old time/op    new time/op    delta
ComplexEgo-24              5.67µs ± 0%    5.78µs ± 0%  +1.91%
ComplexQuicktemplate-24    1.03µs ± 0%    1.01µs ± 0%  -2.22%
ComplexFtmpl-24            7.08µs ± 0%    7.13µs ± 0%  +0.65%
ComplexGorazor-24          5.09µs ± 0%    5.19µs ± 0%  +2.10%
ComplexHero-24              964ns ± 0%     955ns ± 0%  -0.92%
ComplexJade-24              764ns ± 0%     745ns ± 0%  -2.47%

name                     old alloc/op   new alloc/op   delta
ComplexEgo-24                568B ± 0%      568B ± 0%   0.00%
ComplexQuicktemplate-24     0.00B          0.00B        0.00%
ComplexFtmpl-24            3.54kB ± 0%    3.54kB ± 0%   0.00%
ComplexGorazor-24          3.73kB ± 0%    3.69kB ± 0%  -1.07%
ComplexHero-24              0.00B          0.00B        0.00%
ComplexJade-24              0.00B          0.00B        0.00%

name                     old allocs/op  new allocs/op  delta
ComplexEgo-24                31.0 ± 0%      31.0 ± 0%   0.00%
ComplexQuicktemplate-24      0.00           0.00        0.00%
ComplexFtmpl-24              38.0 ± 0%      38.0 ± 0%   0.00%
ComplexGorazor-24            22.0 ± 0%      24.0 ± 0%  +9.09%
ComplexHero-24               0.00           0.00        0.00%
ComplexJade-24               0.00           0.00        0.00%

Security

All packages assume that template authors are trusted. If you allow custom templates you have to sanitize your user input e.g. bluemonday. Generally speaking I would suggest to sanitize every input not just HTML-input.

Attention: This part is not updated since 2016.

FrameworkSecurityComment
AceNo
amberNo
egoPartial (html.EscapeString)only HTML, others need to be called manually
egonPartial (html.EscapeString)only HTML, others need to be called manually
egonslinsoPartial (html.EscapeString)only HTML, others need to be called manually
ftmplPartial (html.EscapeString)only HTML, others need to be called manually
GoYescontextual escaping html/template Security Model
GorazorPartial (template.HTMLEscapeString)only HTML, others need to be called manually
HandlebarsPartial (raymond.escape)only HTML
HeroPartial (html.EscapeString)only HTML, others need to be called manually
JadePartial (html.EscapeString)Autoescape for HTML, others need to be called manually
JetPartial (html.EscapeString)Autoescape for HTML, others need to be called manually
KasiaPartial (kasia.WriteEscapedHtml)only HTML
MustachePartial (template.HTMLEscape)only HTML
Pongo2Partial (pongo2.filterEscape, pongo2.filterEscapejs)autoescape only escapes HTML, others could be implemented as pongo filters
QuicktemplatePartial (html.EscapeString)only HTML, others need to be called manually
SoyPartial (template.HTMLEscapeString, url.QueryEscape, template.JSEscapeString)autoescape only escapes HTML, contextual escaping is defined as a project goal