Benchmark against html/template

This commit is contained in:
Brandon Dyck 2019-05-11 20:37:40 -06:00
parent c00d92595e
commit 0d7bf2514d
2 changed files with 106 additions and 0 deletions

View File

@ -6,6 +6,8 @@ hatmill - HTML generation DSL for Go
[![GoDoc](https://godoc.org/gitlab.codemonkeysoftware.net/b/hatmill?status.svg)](https://godoc.org/gitlab.codemonkeysoftware.net/b/hatmill)
![Badge count](https://img.shields.io/badge/badges-5-yellow.svg)
`hatmill` provides a simple set of types and helper functions for writing HTML in plain Go code, without having to deal with any template languages. It is not spectacularly performant, but is comparable to the `html/template` package (at least in simple cases; run `go test -bench=. -benchmem` for proof). `hatmill` “templates” are arguably easier to read and write than many template languages.
Installation
---------------
There are three necessary packages:

104
benchmark_test.go Normal file
View File

@ -0,0 +1,104 @@
package hatmill_test
import (
"bytes"
h "gitlab.codemonkeysoftware.net/b/hatmill"
he "gitlab.codemonkeysoftware.net/b/hatmill/element"
"html/template"
"io"
"testing"
)
type benchPerson struct {
Name string
FavoriteAnimals []string
}
type benchModel struct {
Title string
Persons []benchPerson
}
var benchData = benchModel{
Title: "People I know",
Persons: []benchPerson{
{Name: "Malter Witty", FavoriteAnimals: []string{"porpoise"}},
{Name: "Perry Moppins", FavoriteAnimals: []string{"penguin"}},
{Name: "Ralbus Fumblemore", FavoriteAnimals: []string{"owl", "cat", "toad"}},
{Name: "Wuce Brayne", FavoriteAnimals: []string{"bat", "robin"}},
},
}
type testingT interface {
Helper()
Error(args ...interface{})
}
func checkError(t testingT, err error) {
t.Helper()
if err != nil {
t.Error(err)
}
}
func stdHtmlTemplate(t testingT) func(testingT, io.Writer, benchModel) {
// hatmill does not pretty-print its output, so neither must we here.
const tpl = `<!DOCTYPE html><html><head><title>{{.Title}}</title></head>` +
`<body>{{range .Persons}}<div><h1>{{.Name}}</h1>` +
`<ul>{{range .FavoriteAnimals}}<li>{{ . }}</li>{{end}}</ul>` +
`</div>{{end}}</body></html>`
compiled, err := template.New("page").Parse(tpl)
checkError(t, err)
return func(t testingT, w io.Writer, model benchModel) {
checkError(t, compiled.Execute(w, model))
}
}
func hatmillTemplate(t testingT, w io.Writer, model benchModel) {
var personHtml h.Terms
for _, p := range model.Persons {
var animalHtml h.Terms
for _, a := range p.FavoriteAnimals {
animalHtml = append(animalHtml, he.Li()(h.Text(a)))
}
personHtml = append(personHtml, he.Div()(
he.H1()(h.Text(p.Name)),
he.Ul()(animalHtml),
))
}
html := he.Html()(
he.Head()(
he.Title()(h.Text(model.Title)),
),
he.Body()(personHtml),
)
_, err := h.WriteDocument(w, html)
checkError(t, err)
}
func TestBenchmarkEquivalence(t *testing.T) {
var stdHtmlOutput bytes.Buffer
stdHtmlTemplate(t)(t, &stdHtmlOutput, benchData)
var hatmillOutput bytes.Buffer
hatmillTemplate(t, &hatmillOutput, benchData)
if stdHtmlOutput.String() != hatmillOutput.String() {
t.Fatalf("Output does not match\nhtml/template: %s\nhatmill: %s",
&stdHtmlOutput, &hatmillOutput)
}
}
func doBenchmark(b *testing.B, tpl func(testingT, io.Writer, benchModel)) {
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
tpl(b, &buf, benchData)
}
}
func BenchmarkStdHtmlTemplate(b *testing.B) {
doBenchmark(b, stdHtmlTemplate(b))
}
func BenchmarkHatmill(b *testing.B) {
doBenchmark(b, hatmillTemplate)
}