Home

Awesome

goTemplateBenchmark

comparing the performance of different template engines

full featured template engines

precompilation to Go code

baseline 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

simple benchmarks

full featured template engines

NameRunsµs/opB/opallocations/op
Ace267,15312.6281,12140
Amber393,5788.56984936
Golang628,2947.88376935
GolangText1,532,5162.3661287
Handlebars266,96013.0463,42475
JetHTML4,546,1530.77300
Mustache776,2634.3801,72330
Pongo2566,5925.7872,07532
Soy955,0033.5091,22419
comparing: go1.22.7 to go1.23.1
name           old time/op    new time/op    delta
Golang-24        8.56µs ± 0%    7.88µs ± 0%  -7.91%
GolangText-24    2.44µs ± 0%    2.37µs ± 0%  -3.11%
Ace-24           13.1µs ± 0%    12.6µs ± 0%  -3.65%
Amber-24         8.90µs ± 0%    8.57µs ± 0%  -3.70%
Mustache-24      4.27µs ± 0%    4.38µs ± 0%  +2.50%
Pongo2-24        5.72µs ± 0%    5.79µs ± 0%  +1.24%
Handlebars-24    13.1µs ± 0%    13.0µs ± 0%  -0.21%
Soy-24           3.47µs ± 0%    3.51µs ± 0%  +1.18%
JetHTML-24        769ns ± 0%     773ns ± 0%  +0.52%

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.42kB ± 0%    3.42kB ± 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      75.0 ± 0%      75.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,811,8271.012858
Ftmpl2,185,8621.60777412
Gomponents654,1054.8751,11256
Gorazor4,444,6260.8205125
HB498,6917.5342,44851
Hero30,477,8580.12000
Jade41,132,3220.08500
Quicktemplate18,337,1610.18000
Templ6,214,0700.557962
comparing: go1.22.7 to go1.23.1
name              old time/op    new time/op    delta
Ego-24              1.00µs ± 0%    1.01µs ± 0%  +1.60%
HB-24               7.48µs ± 0%    7.53µs ± 0%  +0.72%
Quicktemplate-24     176ns ± 0%     180ns ± 0%  +2.45%
Ftmpl-24            1.66µs ± 0%    1.61µs ± 0%  -3.31%
Gorazor-24           829ns ± 0%     820ns ± 0%  -1.13%
Hero-24              118ns ± 0%     120ns ± 0%  +1.52%
Jade-24             87.0ns ± 0%    85.2ns ± 0%  -1.98%
Templ-24             543ns ± 0%     557ns ± 0%  +2.73%
Gomponents-24       4.87µs ± 0%    4.88µs ± 0%  +0.02%

name              old alloc/op   new alloc/op   delta
Ego-24               85.0B ± 0%     85.0B ± 0%   0.00%
HB-24               2.45kB ± 0%    2.45kB ± 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%
Templ-24             96.0B ± 0%     96.0B ± 0%   0.00%
Gomponents-24       1.11kB ± 0%    1.11kB ± 0%   0.00%

name              old allocs/op  new allocs/op  delta
Ego-24                8.00 ± 0%      8.00 ± 0%   0.00%
HB-24                 51.0 ± 0%      51.0 ± 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%
Templ-24              2.00 ± 0%      2.00 ± 0%   0.00%
Gomponents-24         56.0 ± 0%      56.0 ± 0%   0.00%

more complex test with template inheritance (if possible)

full featured template engines

NameRunsµs/opB/opallocations/op
ComplexGolang58,86068.7656,565290
ComplexGolangText112,90230.1052,236107
ComplexJetHTML374,07612.2925355
ComplexMustache149,06426.1797,276156
comparing: go1.22.7 to go1.23.1
name                  old time/op    new time/op    delta
ComplexGolang-24        69.4µs ± 0%    68.8µs ± 0%  -0.95%
ComplexGolangText-24    29.0µs ± 0%    30.1µs ± 0%  +3.86%
ComplexMustache-24      25.7µs ± 0%    26.2µs ± 0%  +1.99%
ComplexJetHTML-24       12.2µs ± 0%    12.3µs ± 0%  +0.52%

name                  old alloc/op   new alloc/op   delta
ComplexGolang-24        6.57kB ± 0%    6.57kB ± 0%   0.00%
ComplexGolangText-24    2.24kB ± 0%    2.24kB ± 0%   0.00%
ComplexMustache-24      7.28kB ± 0%    7.28kB ± 0%  +0.01%
ComplexJetHTML-24         534B ± 0%      535B ± 0%  +0.19%

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
ComplexEgo1,000,0005.03456931
ComplexFtmpl469,3637.2543,53638
ComplexGoDirectBuffer6,702,9370.52000
ComplexGorazor606,1705.5723,68824
ComplexHero3,914,1550.92600
ComplexJade4,583,8690.74200
ComplexQuicktemplate3,441,3011.06500
ComplexTempl1,325,7912.78140811
comparing: go1.22.7 to go1.23.1
name                      old time/op    new time/op    delta
ComplexEgo-24               4.98µs ± 0%    5.03µs ± 0%  +1.12%
ComplexQuicktemplate-24     1.04µs ± 0%    1.06µs ± 0%  +2.60%
ComplexTempl-24             2.80µs ± 0%    2.78µs ± 0%  -0.54%
ComplexFtmpl-24             7.19µs ± 0%    7.25µs ± 0%  +0.86%
ComplexGorazor-24           5.47µs ± 0%    5.57µs ± 0%  +1.86%
ComplexHero-24               937ns ± 0%     926ns ± 0%  -1.11%
ComplexJade-24               738ns ± 0%     742ns ± 0%  +0.57%
ComplexGoDirectBuffer-24     520ns ± 0%     520ns ± 0%  +0.10%

name                      old alloc/op   new alloc/op   delta
ComplexEgo-24                 569B ± 0%      569B ± 0%   0.00%
ComplexQuicktemplate-24      0.00B          0.00B        0.00%
ComplexTempl-24               408B ± 0%      408B ± 0%   0.00%
ComplexFtmpl-24             3.54kB ± 0%    3.54kB ± 0%   0.00%
ComplexGorazor-24           3.69kB ± 0%    3.69kB ± 0%   0.00%
ComplexHero-24               0.00B          0.00B        0.00%
ComplexJade-24               0.00B          0.00B        0.00%
ComplexGoDirectBuffer-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%
ComplexTempl-24               11.0 ± 0%      11.0 ± 0%   0.00%
ComplexFtmpl-24               38.0 ± 0%      38.0 ± 0%   0.00%
ComplexGorazor-24             24.0 ± 0%      24.0 ± 0%   0.00%
ComplexHero-24                0.00           0.00        0.00%
ComplexJade-24                0.00           0.00        0.00%
ComplexGoDirectBuffer-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