5.4 KiB
Performance benchmarks
Benchmarks measure time and Go allocations for repeatable operations. Run the
relevant cases before and after performance changes, alongside correctness tests.
Normal go test runs do not execute benchmarks; add -bench explicitly.
Browser document benchmarks
Run from the repository root:
go test ./internal/review -run '^$' -bench '^BenchmarkDocument(Snapshot|HeadingNavigation|LineAssembly)$' -benchmem -count=10 > /tmp/cthulhu-bench-before.txt
Repeat after the change with the same command and
/tmp/cthulhu-bench-after.txt as the output file. Choose distinct filenames when
retaining earlier measurements. To check that the benchmarks execute quickly,
use -benchtime=1x -count=1; that is a smoke check, not a useful timing baseline.
The initial cases use 100, 1,000 and 10,000 content objects, plus one section per twenty objects and a document root. Fixtures contain headings, links and Unicode text, with two objects on each visual row. Each benchmark iteration performs:
BenchmarkDocumentSnapshot: one document snapshot from an existing tree, including traversal and derived metadata.BenchmarkDocumentHeadingNavigation: a forward/backward heading round trip on an existing tree. Theadjacentcase visits two headings near the middle;wrapmoves between the last and first headings. Each iteration returns to the same position, keeping the workload independent of the loop count. Time and allocations are per round trip (two moves), including snapshot construction and navigation bookkeeping.BenchmarkDocumentLineAssembly: one line lookup in the middle of a document, combining the two text objects on that row. This includes snapshot construction.BenchmarkDocumentCaretNavigation: a character forward/backward round trip within the same text object. Reusable caret ranges are warmed before timing.BenchmarkDocumentWorkerHeadingNavigation: a heading forward/backward round trip through the review worker, including focus synchronization, diagnostics and placement dispatch, with an in-memory provider. It performs no browser IPC.
Include the additional cases with
-bench '^BenchmarkDocument(Snapshot|HeadingNavigation|LineAssembly|CaretNavigation|WorkerHeadingNavigation)$'.
Navigation shares an immutable indexed view; BenchmarkDocumentSnapshot
continues to measure the independently owned node copy returned by Document.
The first view construction is included in snapshot and line measurements and
amortized over subsequent iterations. A short run can therefore overstate the
steady allocation rate. See document navigation validation
for cache ownership, invalidation and real-browser checks.
Fixture construction and initial focus synchronization are outside the timed loops. The benchmarks fail on errors, missing work or invalid result shapes. They use owned tree data and do not start a browser, access the desktop reader, query native AT-SPI objects, or produce speech or Braille output. They do not measure browser capture, native caret placement, input queues or audible latency. Expand the suite when a reproduced bottleneck needs a different workload, such as nested documents, tables, long text or event bursts.
Existing benchmarks also cover speech text preparation and Tentacle commands:
go test ./internal/speech -run '^$' -bench '^BenchmarkNormalizeSpeakText$' -benchmem -count=10
go test ./internal/tentacle -run '^$' -bench '^BenchmarkCommandRoundTrip$' -benchmem -count=10
The Tentacle benchmark starts disposable test peers and measures one completed command at a time across 1, 8 or 16 processes; it does not measure concurrent command saturation.
Comparing changes
Record the revision, uncommitted changes, Go version, machine and benchmark
command with results. Compare repeated measurements on the same machine and
toolchain, under similar load and power settings. Do not run performance
comparisons with the race detector or coverage instrumentation enabled. Use
benchstat from golang.org/x/perf/cmd/benchstat, if installed, to compare files:
benchstat /tmp/cthulhu-bench-before.txt /tmp/cthulhu-bench-after.txt
ns/op is elapsed time per operation, B/op is Go bytes allocated per operation,
and allocs/op counts Go allocations. These are not total process memory or
native C allocation measurements. A benchmark's PASS means it executed without
failure; it does not mean its performance is acceptable or improved.
Investigate substantial, repeatable regressions before changing code. Profile the affected operation when the cause is unclear. Preserve the full workload and result checks when optimizing; do not improve scores by dropping required content or bypassing validation. Initially keep timings informational rather than adding timing gates to tests or push hooks. These commands can be run by automation later; no scheduled job or CI timing gate is configured here.
For this benchmark-only change, run the existing affected correctness tests:
go test ./internal/review ./internal/atspi -count=1
Future browser performance fixes also need the relevant native browser harness
and human checks: navigate headings forward and backward through wrapping, read
lines with arrows, move through links and editable fields, return from browser
chrome, and verify fresh input interrupts old output. Check speech and physical
Braille where available. Automated timing does not replace those checks or the
human review required by CONTRIBUTING.md.