Skip to main content
Version: 1.1

Benchmarks

What the code costs, as opposed to what it does and what the suite reached.

Measured on 2026-08-31 at commit 481d94ff066a, by the release that published this site.

This release measured 25 benchmarks across 3 modules, on linux/amd64, AMD EPYC 7763 64-Core Processor and linux/amd64, INTEL(R) XEON(R) PLATINUM 8573C.

Every number on this page is measured rather than written. A benchmark pass runs go test -run '^$' -bench . -benchmem per module, in a stage of its own, and the release folds the stream into the same report the coverage and test-results pages read. Nothing here is a figure someone typed into a document, which matters more for a performance claim than for almost any other kind: a benchmark number written by hand is a number about a machine that no longer exists.

-run '^$' is not optional. Without it every test runs again alongside the benchmarks, and the timings become numbers about a machine that was busy doing something else. -benchtime is left at Go's own default, because a measurement taken over fewer iterations than the tool chose is a measurement nobody can compare.

By module

pkg/ccme

linux/amd64, AMD EPYC 7763 64-Core Processor

BenchmarkTime/opBytes/opAllocs/opIterations
BenchmarkParseCRLF5.1 µs1.2 KiB5268,934
BenchmarkParseMultiUnit34.5 µs3.8 KiB1234,166
BenchmarkParseSimple6.6 µs1.2 KiB5279,295
BenchmarkParseSubject4.8 µs832 B2800,246

pkg/config

linux/amd64, AMD EPYC 7763 64-Core Processor

BenchmarkTime/opBytes/opAllocs/opIterations
BenchmarkApplyEdits/json3.0 ms13.5 KiB118417
BenchmarkDecode/fold-hit214.6 µs28.5 KiB54010,000
BenchmarkDecode/full242.3 µs28.4 KiB5148,815
BenchmarkDecode/small5.0 µs1.0 KiB22253,243
BenchmarkFold/mixed85.43 ns8 B112,792,060
BenchmarkFold/unicode248.2 ns16 B14,715,982
BenchmarkOverrides78.7 µs12.2 KiB7215,931
BenchmarkReadTree/json/large3.3 ms413.0 KiB5,635516
BenchmarkReadTree/json/medium211.0 µs32.1 KiB5827,767
BenchmarkReadTree/json/small36.1 µs3.6 KiB7259,445
BenchmarkReadTree/toml/large9.3 ms681.7 KiB8,624134
BenchmarkReadTree/yaml/large14.9 ms1.1 MiB19,62668
BenchmarkReadTree/yaml/small109.5 µs16.8 KiB24410,000
BenchmarkRefWidth/222.5 µs3.6 KiB5860,968
BenchmarkRefWidth/32308.3 µs41.8 KiB6355,562
BenchmarkSettings/empty-objects61.7 µs6.6 KiB519,348

services/dispat

linux/amd64, INTEL(R) XEON(R) PLATINUM 8573C

BenchmarkTime/opBytes/opAllocs/opIterations
BenchmarkComputeGroup/fixed2.5 ms560.1 KiB3,415482
BenchmarkComputeGroup/fixedMajor1.3 ms562.3 KiB3,410817
BenchmarkComputeGroup/fixedMajorMinor2.7 ms562.6 KiB3,412381
BenchmarkComputeGroup/fixedMajorSparse845.1 µs458.6 KiB2,1931,339
BenchmarkComputeGroup/fixedSparse2.0 ms459.2 KiB2,197691

A row is one benchmark function, or one case of a table-driven one, named as its source names it. The iteration count is a column rather than a footnote because it is what says how much to trust the row beside it: a benchmark the tool ran ten times and one it ran ten million times are not the same kind of number. The machine is stated per module for the same reason — a nanosecond figure without the CPU that produced it is a number nobody can compare anything to, and a release runner is not a workstation.

Allocations are the figure to read first. A time depends on the machine, the toolchain and whatever else was running; an allocation count is a property of the code, and it moves only when someone changes what the code does. That is why several of these packages pin their allocation counts as tests — a budget with one allocation of headroom, so a toolchain change does not fail a build for a rounding difference while a reintroduced copy fails it immediately.

Fuzz targets

TargetPackageCorpus entries
FuzzNormalizepkg/ccme114
FuzzParsepkg/ccme116
FuzzParseSubjectpkg/ccme114
FuzzParserspkg/config9
FuzzRefTargetspkg/config8
FuzzSettingspkg/config5
FuzzGradleLinepkg/scanner16
FuzzPBXSettingpkg/scanner9
FuzzParserspkg/scanner39
FuzzPep508Deppkg/scanner8
FuzzPodDeclarationpkg/scanner12
FuzzPodspecStatementpkg/scanner10
FuzzLinkpkg/writer12
FuzzReplaceBytespkg/writer8
FuzzRewriteAndroidManifestpkg/writer7
FuzzRewriteCargopkg/writer8
FuzzRewriteComposepkg/writer9
FuzzRewriteComposerpkg/writer6
FuzzRewriteCsprojpkg/writer6
FuzzRewriteDockerfilepkg/writer9
FuzzRewriteGemfilepkg/writer6
FuzzRewriteGodotCfgpkg/writer10
FuzzRewriteGradleBuildpkg/writer8
FuzzRewriteGradleCatalogpkg/writer8
FuzzRewriteMavenpkg/writer7
FuzzRewriteNpmpkg/writer8
FuzzRewriteNuGetListspkg/writer6
FuzzRewriteNuspecpkg/writer6
FuzzRewritePlistpkg/writer8
FuzzRewritePodfilepkg/writer8
FuzzRewritePubspecpkg/writer6
FuzzRewritePyprojectpkg/writer9
FuzzRewriteUPluginpkg/writer8
FuzzRewriteUnityProjectSettingspkg/writer8
FuzzRewriteXcodeProjpkg/writer7

A fuzz target under a plain go test runs its corpus rather than fuzzing: the seeds the test declares with f.Add, plus whatever testdata/fuzz holds for it — the inputs earlier fuzzing sessions found interesting, kept as regression cases. The corpus column is what every suite run exercises, and it is the honest half of the number. It is not a claim that a fuzzing session ran here, because none did.

Fuzzing itself is a thing somebody does on purpose, for as long as they have:

go test ./... -run '^$' -fuzz FuzzParse -fuzztime 10m

A crash it finds is written into testdata/fuzz as a new corpus entry, committed, and from then on every run of the ordinary suite replays it. That is why the corpus grows and why the count above is worth reading: it is the number of inputs the suite is holding the parsers to, not the number of inputs anyone has tried.

Reproducing

The whole measurement is one command per module from the monorepo root, and it is the one the release runs:

go run github.com/yohimik/dispat/tools/testreport bench config -- ./... -run '^$' -bench . -benchmem
go run github.com/yohimik/dispat/tools/testreport build # writes packages/docs/data/report.json, read by this page

The stream lands in coverage/benchlog/<module>.json, beside the go test -json logs the suites leave in coverage/testlog/. A module measured by a run replaces whatever it had; a module the run did not measure keeps it, so a release that rebuilt only part of the monorepo does not blank a page. Rebuilding this site with packages/docs/data/report.json in place is what puts the figures back; without one, the prose stays and the tables are left out.

What a benchmark here is for

These are regression gates, not marketing. A benchmark exists in this repository when there is a specific mistake it would catch: a second deep copy of a parsed document, a flat intermediate map materialised only to be rebuilt, a per-element allocation in a loop that could have hoisted it. The corresponding allocation budget is what turns the benchmark into a gate — the benchmark says what it costs, and the budget fails the build when the cost changes for a reason nobody intended.

What none of them are is a comparison against another library. A benchmark measuring two things measures the harness between them, and this repository has no honest way to run somebody else's code on the same terms.