Documentation
¶
Overview ¶
Package microbench is the schema, `go test -bench` parser, and encoders for the framework-tax microbenchmark (issue #141 §13 A): the per-request abstraction cost of each layer — net/http → Gin → Huma → Gombit — across the five scenarios (plaintext, json, path-param, valid-post, invalid-post), reported as ns/op, B/op, and allocs/op.
Each stack is a separate `go test` process (constructing framework.App mutates a process-global; see benchmarks/micro/gombit), so the parser tags rows with the stack the orchestrator ran, and rows merge by stack (a stack is replaced whole, so a partial re-run can't leave stale scenarios behind). Every `-count` ns/op sample is kept, not just the last, so a statistical summary (median/CoV, benchstat) is a non-breaking addition rather than lost data. Keeping the parse + schema here (not in the shell) lets it be unit-tested against captured `go test -bench` output.
Index ¶
Constants ¶
const SchemaVersion = 1
SchemaVersion is bumped on any breaking change to the row shape (issue §9).
Variables ¶
var Scenarios = []string{"plaintext", "json", "path-param", "valid-post", "invalid-post"}
Scenarios is the full set every stack must report; a run missing any of these is incomplete and rejected rather than published as a partial row set.
Functions ¶
Types ¶
type Row ¶
type Row struct {
SchemaVersion int `json:"schema_version"`
Stack string `json:"stack"`
Scenario string `json:"scenario"`
NsPerOp []float64 `json:"ns_per_op"`
BytesPerOp int64 `json:"bytes_per_op"`
AllocsPerOp int64 `json:"allocs_per_op"`
}
Row is one (stack, scenario) measurement. NsPerOp holds every `-count` sample (so median/variance is derivable and nothing is thrown away); B/op and allocs/op are deterministic for these benchmarks and stored as scalars.
func Merge ¶
Merge returns existing with every row for the incoming stacks removed and the incoming rows appended — a stack is replaced as a whole, so a re-run can't leave a stale scenario from a previous run. Deterministically sorted.
func MergeStack ¶ added in v0.1.14
MergeStack replaces a whole stack *namespace* with incoming and appends it, preserving unrelated stacks. It clears every existing row whose stack is exactly namespace or lies under it (namespace + "/..."), then adds incoming.
This is what a single benchmark run persists through: a framework-tax run (namespace "gin") owns exactly one stack, while an ablation run (namespace "gombit-ablation") owns a whole dynamic ladder of `gombit-ablation/<row>` stacks derived from the runtime middleware stack. Plain Merge only drops the exact stacks present in this run's output, so a layer removed or renamed between runs would leave its `gombit-ablation/<old>` row orphaned in the authoritative JSON forever. Clearing the namespace makes the persisted ladder track the runtime-derived one. The "/" boundary keeps sibling namespaces safe: clearing "gombit" never touches "gombit-ablation/xss", and clearing "gombit-ablation" never touches "gombit".
func ParseBenchOutput ¶
ParseBenchOutput reads `go test -bench` text output and returns a row per (stack, scenario) pair found, accumulating every iteration's ns/op into NsPerOp. Non-benchmark lines are ignored. Two benchmark name shapes are recognized:
- BenchmarkFrameworkTax/<scenario> — the four-row framework-tax matrix (net/http, gin, huma, gombit). Every line in one `go test` process belongs to the single stack passed as the stack argument, so that argument names the row directly.
- BenchmarkAblation/gombit-ablation/<layer>/<scenario> — the Gombit per-layer middleware ablation benchmark (benchmarks/micro/gombit/ablation_bench_test.go). A single `go test` process reports many stacks at once (one per progressively-stacked layer), so each line's stack ("gombit-ablation/<layer>") is parsed out of the benchmark name itself rather than taken from the stack argument.
It errors unless every discovered stack reports every scenario in Scenarios (a panic mid-run yields fewer scenarios, which must fail rather than publish a partial stack), and unless at least one recognized benchmark line was found at all.
func (Row) CoVNsPerOp ¶
CoVNsPerOp is the coefficient of variation (sample stddev / mean) of the ns/op samples — a stability signal. A high value means the run was noisy (a non-stationary series on a contended host), so the median should be distrusted: e.g. a layering-cost inversion (Huma cheaper than the Gin it sits on) is only ever noise. Fewer than two samples or a zero mean → 0.
func (Row) MedianNsPerOp ¶
MedianNsPerOp is the median of the ns/op samples (0 if none) — the headline statistic, matching the CRUD table's use of the median.