/goTemplateBenchmark

comparing the performance of different template engines

Primary LanguageGo

goTemplateBenchmark

comparing the performance of different template engines

full featured template engines

precompilation to Go code

special benchmarks for comparison

  • Go text/template (do not use this for HTML)
  • StaticString - Use one static string for the whole Template to have a base time
  • DirectBuffer - Use go to write the HTML by hand to the buffer

transpiling to Go Template

  • Damsel I won't benchmark transpiling engines, because transpilation should just happen once at startup. If you cache the transpilation result, which is recommended, you would have the same performance numbers as html/template for rendering.

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.

Results dev machine

local desktop: ryzen 3900x

special benchmarks

Name Runs ns/op B/op allocations/op
ComplexGoDirectBuffer 8,293,862 432 0 0
ComplexGoStaticString 296,413,996 12 0 0
comparing: go1.16.7 to go version go1.17 linux/amd64
name                      old time/op    new time/op    delta
ComplexGoDirectBuffer-24     457ns ± 0%     432ns ± 0%  -5.36%
ComplexGoStaticString-24    12.5ns ± 0%    11.7ns ± 0%  -6.32%

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

Name Runs µs/op B/op allocations/op
Ace 281,271 12.762 1,121 40
Amber 465,156 8.673 849 36
Golang 428,677 8.380 769 35
GolangText 1,337,016 2.641 128 7
Handlebars 251,836 14.019 3,967 78
JetHTML 3,901,444 0.866 0 0
Mustache 842,256 4.163 1,530 29
Pongo2 599,228 5.833 2,074 32
Soy 945,814 3.752 1,320 20
comparing: go1.16.7 to go version go1.17 linux/amd64
name           old time/op    new time/op    delta
Golang-24        8.07µs ± 0%    8.38µs ± 0%   +3.84%
GolangText-24    2.60µs ± 0%    2.64µs ± 0%   +1.54%
Ace-24           12.7µs ± 0%    12.8µs ± 0%   +0.22%
Amber-24         8.37µs ± 0%    8.67µs ± 0%   +3.58%
Mustache-24      4.25µs ± 0%    4.16µs ± 0%   -2.05%
Pongo2-24        6.03µs ± 0%    5.83µs ± 0%   -3.25%
Handlebars-24    14.0µs ± 0%    14.0µs ± 0%   -0.01%
Soy-24           4.05µs ± 0%    3.75µs ± 0%   -7.34%
JetHTML-24       1.00µs ± 0%    0.87µs ± 0%  -13.21%

name           old alloc/op   new alloc/op   delta
Golang-24          897B ± 0%      769B ± 0%  -14.27%
GolangText-24      128B ± 0%      128B ± 0%    0.00%
Ace-24           1.25kB ± 0%    1.12kB ± 0%  -10.25%
Amber-24           977B ± 0%      849B ± 0%  -13.10%
Mustache-24      1.53kB ± 0%    1.53kB ± 0%    0.00%
Pongo2-24        2.07kB ± 0%    2.07kB ± 0%    0.00%
Handlebars-24    3.97kB ± 0%    3.97kB ± 0%    0.00%
Soy-24           1.32kB ± 0%    1.32kB ± 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        29.0 ± 0%      29.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             20.0 ± 0%      20.0 ± 0%    0.00%
JetHTML-24         0.00           0.00         0.00%

precompilation to Go code

Name Runs µs/op B/op allocations/op
Ego 2,917,192 1.214 85 8
EgonSlinso 18,751,664 0.192 0 0
Ftmpl 2,187,069 1.670 1,095 12
Gorazor 4,525,616 0.793 512 5
Hero 28,386,926 0.127 0 0
Jade 40,868,552 0.086 0 0
Quicktemplate 12,820,016 0.257 0 0
comparing: go1.16.7 to go version go1.17 linux/amd64
name              old time/op    new time/op    delta
Ego-24              1.32µs ± 0%    1.21µs ± 0%   -8.10%
EgonSlinso-24        315ns ± 0%     192ns ± 0%  -38.94%
Quicktemplate-24     263ns ± 0%     257ns ± 0%   -2.06%
Ftmpl-24            1.70µs ± 0%    1.67µs ± 0%   -1.76%
Gorazor-24           827ns ± 0%     793ns ± 0%   -4.12%
Hero-24              129ns ± 0%     127ns ± 0%   -1.24%
Jade-24             85.8ns ± 0%    86.2ns ± 0%   +0.43%

name              old alloc/op   new alloc/op   delta
Ego-24               85.0B ± 0%     85.0B ± 0%    0.00%
EgonSlinso-24        0.00B          0.00B         0.00%
Quicktemplate-24     0.00B          0.00B         0.00%
Ftmpl-24            1.09kB ± 0%    1.09kB ± 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%
EgonSlinso-24         0.00           0.00         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

Name Runs µs/op B/op allocations/op
ComplexGolang 49,048 72.884 6,643 290
ComplexGolangText 103,266 33.945 2,235 107
ComplexJetHTML 239,065 14.866 534 5
ComplexMustache 131,143 28.025 7,399 155
comparing: go1.16.7 to go version go1.17 linux/amd64
name                  old time/op    new time/op    delta
ComplexGolang-24        71.9µs ± 0%    72.9µs ± 0%   +1.42%
ComplexGolangText-24    30.6µs ± 0%    33.9µs ± 0%  +11.04%
ComplexMustache-24      27.7µs ± 0%    28.0µs ± 0%   +1.15%
ComplexJetHTML-24       14.8µs ± 0%    14.9µs ± 0%   +0.32%

name                  old alloc/op   new alloc/op   delta
ComplexGolang-24        7.77kB ± 0%    6.64kB ± 0%  -14.54%
ComplexGolangText-24    2.53kB ± 0%    2.23kB ± 0%  -11.73%
ComplexMustache-24      7.40kB ± 0%    7.40kB ± 0%   -0.01%
ComplexJetHTML-24         534B ± 0%      534B ± 0%    0.00%

name                  old allocs/op  new allocs/op  delta
ComplexGolang-24           285 ± 0%       290 ± 0%   +1.75%
ComplexGolangText-24       102 ± 0%       107 ± 0%   +4.90%
ComplexMustache-24         155 ± 0%       155 ± 0%    0.00%
ComplexJetHTML-24         5.00 ± 0%      5.00 ± 0%    0.00%

precompilation to Go code

Name Runs µs/op B/op allocations/op
ComplexEgo 616,015 6.026 568 31
ComplexEgoSlinso 1,475,832 2.414 160 2
ComplexFtmpl 493,070 7.440 4,912 38
ComplexGorazor 870,666 4.248 2,720 21
ComplexHero 3,547,309 1.000 0 0
ComplexJade 4,559,787 0.744 0 0
ComplexQuicktemplate 3,535,020 0.952 0 0
comparing: go1.16.7 to go version go1.17 linux/amd64
name                     old time/op    new time/op    delta
ComplexEgo-24              6.33µs ± 0%    6.03µs ± 0%   -4.85%
ComplexQuicktemplate-24    1.37µs ± 0%    0.95µs ± 0%  -30.51%
ComplexEgoSlinso-24        2.88µs ± 0%    2.41µs ± 0%  -16.24%
ComplexFtmpl-24            7.66µs ± 0%    7.44µs ± 0%   -2.81%
ComplexGorazor-24          4.52µs ± 0%    4.25µs ± 0%   -6.12%
ComplexHero-24             1.01µs ± 0%    1.00µs ± 0%   -1.19%
ComplexJade-24              674ns ± 0%     744ns ± 0%  +10.32%

name                     old alloc/op   new alloc/op   delta
ComplexEgo-24                568B ± 0%      568B ± 0%    0.00%
ComplexQuicktemplate-24     0.00B          0.00B         0.00%
ComplexEgoSlinso-24          160B ± 0%      160B ± 0%    0.00%
ComplexFtmpl-24            4.91kB ± 0%    4.91kB ± 0%    0.00%
ComplexGorazor-24          2.72kB ± 0%    2.72kB ± 0%    0.00%
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%
ComplexEgoSlinso-24          2.00 ± 0%      2.00 ± 0%    0.00%
ComplexFtmpl-24              38.0 ± 0%      38.0 ± 0%    0.00%
ComplexGorazor-24            21.0 ± 0%      21.0 ± 0%    0.00%
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.

Framework Security Comment
Ace No
amber No
ego Partial (html.EscapeString) only HTML, others need to be called manually
egon Partial (html.EscapeString) only HTML, others need to be called manually
egonslinso Partial (html.EscapeString) only HTML, others need to be called manually
ftmpl Partial (html.EscapeString) only HTML, others need to be called manually
Go Yes contextual escaping html/template Security Model
Gorazor Partial (template.HTMLEscapeString) only HTML, others need to be called manually
Handlebars Partial (raymond.escape) only HTML
Hero Partial (html.EscapeString) only HTML, others need to be called manually
Jade Partial (html.EscapeString) Autoescape for HTML, others need to be called manually
Jet Partial (html.EscapeString) Autoescape for HTML, others need to be called manually
Kasia Partial (kasia.WriteEscapedHtml) only HTML
Mustache Partial (template.HTMLEscape) only HTML
Pongo2 Partial (pongo2.filterEscape, pongo2.filterEscapejs) autoescape only escapes HTML, others could be implemented as pongo filters
Quicktemplate Partial (html.EscapeString) only HTML, others need to be called manually
Soy Partial (template.HTMLEscapeString, url.QueryEscape, template.JSEscapeString) autoescape only escapes HTML, contextual escaping is defined as a project goal