Files
interpres/bench_test.go
T

206 lines
4.6 KiB
Go
Raw Normal View History

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package interpres
import (
"context"
"fmt"
"strings"
"testing"
"time"
)
// benchDoc is a representative configuration document: every scalar kind, an
// inline table, sub-tables, and an array of tables.
var benchDoc = []byte(`title = "benchmark configuration"
replicas = 3
ratio = 0.75
enabled = true
when = 2026-09-17T12:00:00Z
local = 2026-09-17T12:00:00
tags = ["alpha", "beta", "gamma"]
limits = { cpu = 4, memory = 1024 }
[server]
host = "localhost"
port = 8080
hosts = ["a.example", "b.example"]
[server.tls]
enabled = true
cert = "/etc/cert.pem"
[[items]]
name = "first"
weight = 10
flags = ["x", "y"]
[[items]]
name = "second"
weight = 20
flags = ["z"]
`)
// longDoc is generated once so the large-input benchmarks measure parsing,
// not document construction. Roughly 2000 array-of-tables entries.
var longDoc = func() []byte {
var b strings.Builder
b.WriteString("title = \"long\"\n")
for i := range 2000 {
fmt.Fprintf(&b, "[[entry]]\nname = \"entry-%d\"\nweight = %d\nwhen = 2026-09-17T12:00:00Z\nratio = 0.5\ntags = [\"a\", \"b\", \"c\"]\n\n", i, i)
}
return []byte(b.String())
}()
type benchTLS struct {
Enabled bool `toml:"enabled"`
Cert string `toml:"cert"`
}
type benchServer struct {
Host string `toml:"host"`
Port int `toml:"port"`
Hosts []string `toml:"hosts"`
TLS benchTLS `toml:"tls"`
}
type benchItem struct {
Name string `toml:"name"`
Weight int `toml:"weight"`
Flags []string `toml:"flags"`
}
type benchConfig struct {
Title string `toml:"title"`
Replicas int `toml:"replicas"`
Ratio float64 `toml:"ratio"`
Enabled bool `toml:"enabled"`
When time.Time `toml:"when"`
Local LocalDateTime `toml:"local"`
Tags []string `toml:"tags"`
Limits map[string]any `toml:"limits"`
Server benchServer `toml:"server"`
Items []benchItem `toml:"items"`
}
func BenchmarkParse(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(len(benchDoc)))
for b.Loop() {
if _, err := ParseMap(benchDoc); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkMarshal(b *testing.B) {
tree, err := ParseMap(benchDoc)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.SetBytes(int64(len(benchDoc)))
for b.Loop() {
if _, err := Marshal(tree); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkStrictDecode(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
var cfg benchConfig
if err := Unmarshal(benchDoc, &cfg, RejectUnknownFields(true)); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkParseLong(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(len(longDoc)))
for b.Loop() {
if _, err := ParseMap(longDoc); err != nil {
b.Fatal(err)
}
}
}
// benchLongEntry mirrors one [[entry]] element of longDoc for the typed
// decode of the long document.
type benchLongEntry struct {
Name string `toml:"name"`
Weight int `toml:"weight"`
When time.Time `toml:"when"`
Ratio float64 `toml:"ratio"`
Tags []string `toml:"tags"`
}
type benchLongDoc struct {
Title string `toml:"title"`
Entry []benchLongEntry `toml:"entry"`
}
func BenchmarkStrictDecodeLong(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(len(longDoc)))
for b.Loop() {
var doc benchLongDoc
if err := Unmarshal(longDoc, &doc, RejectUnknownFields(true)); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkMarshalLong(b *testing.B) {
tree, err := ParseMap(longDoc)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.SetBytes(int64(len(longDoc)))
for b.Loop() {
if _, err := Marshal(tree); err != nil {
b.Fatal(err)
}
}
}
// BenchmarkStrictDecodeTree measures the reference path the targeted decode
// is measured against: the full tree parse followed by the reflection walk.
// The pair runs in one process, so the A/B comparison shares the machine.
func BenchmarkStrictDecodeTree(b *testing.B) {
dec := newDecoder()
dec.disallowUnknown = true
b.ReportAllocs()
for b.Loop() {
tree, _, err := parseWithOptions(context.Background(), benchDoc, parseOptions{}, false)
if err != nil {
b.Fatal(err)
}
var cfg benchConfig
if err := dec.decode(tree, &cfg); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkStrictDecodeTreeLong(b *testing.B) {
dec := newDecoder()
dec.disallowUnknown = true
b.ReportAllocs()
b.SetBytes(int64(len(longDoc)))
for b.Loop() {
tree, _, err := parseWithOptions(context.Background(), longDoc, parseOptions{}, false)
if err != nil {
b.Fatal(err)
}
var doc benchLongDoc
if err := dec.decode(tree, &doc); err != nil {
b.Fatal(err)
}
}
}