Experiment: 2dfc41f
rad experiment show 2dfc41fdc3d2e29a2f02765d408d6d6b31fde2b3
by ee@did:key:z6MkvsiybCuk1WDZhh2aXDx7NTiZejKgWZygMNsUuXDMNvVQ · Apr 28 07:14 2026
Avoid recomputing message hashes on the hot path when cached transcript hashes already exist; profiling showed the previous fallback assignment was still calling hashMessage for every message every redraw.
Measurements
MetricBaselineThe benchmark measurement of the unmodified codeCandidateThe code with the proposed optimization appliedCandidate/Parent ΔThe performance change from baseline to candidate
view_build_ns (ns) primary
Reduce the steady-state cost of `internal/tui.View.BuildWithAnchors` on a large, cached transcript. This is on the hot path for every TUI redraw while typing and streaming, so repeated builds over mostly-unchanged messages should be as cheap as possible.
367957.000 ns (n=1)35303.000 ns (n=1)-90.40%
view_build_B secondary114628.000 (n=1)111173.000 (n=1)-3.01% REGRESSED
view_build_allocs secondary42.000 (n=1)16.000 (n=1)-61.90% REGRESSED
Annotations
hypothesisAfter incremental transcript caches, remaining steady-state time is mostly accidental hash recomputation inside BuildWithAnchors; only computing hashes on cache miss should collapse ns/op further.
memory_changeallocs 42 -> 16, bytes 114628 -> 111173
next_action_hintIf continuing, prewarm the benchmark to remove the single cold-build outlier and then inspect remaining allocations in anchor/output slice construction.
profiling_signalpprof showed ~88% of CPU in hashMessage/fnv64aWrite after the first optimization
speedup_factor_vs_baseline20.28x faster ns/op
speedup_factor_vs_previous10.42x faster ns/op
Base CommitThe starting commit before the optimization was appliedfc388be7482160dc2f77c014bbc45335b690a918
Candidate CommitThe code with the proposed optimization applied6e3f8720042e54b02999bd7dee205317359238b4
Bench script5285acce968f
bench_cmdbash autoresearch.sh
Schemav5
Diff
+ .community-computer/published.json
@@ -0,0 +1,9 @@
1+{ 2+ "published": [ 3+ { 4+ "base": "89c9750ccf88f432c6bb0e6bb8f1176d536d04fb", 5+ "head": "fc388be7482160dc2f77c014bbc45335b690a918", 6+ "cob": "07c118cd25063f79bf8aeab46d3dd5bfacfb1fec" 7+ } 8+ ] 9+}
~ autoresearch.jsonl
@@ -1,2 +1,3 @@
1 {"type":"config","name":"Speed up TUI transcript rendering","metricName":"view_build_ns","metricUnit":"ns","bestDirection":"lower"} 2 {"run":1,"commit":"89c9750","metric":716061,"metrics":{"view_build_B":405548,"view_build_allocs":6446},"status":"keep","description":"Baseline benchmark harness for steady-state View.BuildWithAnchors; benchmark works but autoresearch.sh mis-parsed go test columns for primary metric and bytes.","timestamp":1777360240079,"segment":0,"confidence":null,"iterationTokens":98,"asi":{"hypothesis":"Establish a stable benchmark for cached large-transcript TUI rendering before optimizing view.go.","benchmark_shape":"120 synthetic user/assistant/tool triplets with markdown, tool args, and tool results","parsing_issue":"go test -benchmem columns shifted; script captured B/op token instead of numeric ns/op","next_action_hint":"Fix metric extraction in autoresearch.sh, rerun baseline, then optimize transcript-derived caches."}} 3+{"run":2,"commit":"fc388be","metric":367957,"metrics":{"view_build_B":114628,"view_build_allocs":42},"status":"keep","description":"Cache transcript-derived View metadata incrementally and fix benchmark metric parsing; avoids rehashing messages and reparsing tool-call JSON on every steady-state redraw.","timestamp":1777360408271,"segment":0,"confidence":null,"iterationTokens":13150,"asi":{"hypothesis":"The cached-render path is dominated by repeated transcript bookkeeping rather than markdown rendering, so caching tool metadata, message hashes, and assistant turn state should cut redraw cost sharply.","speedup_factor_vs_baseline":"1.95x faster ns/op","memory_reduction":"allocs 6446 -> 42, bytes 405548 -> 114628 in benchmark","why_it_worked":"BuildWithAnchors now reuses transcript-derived data when Messages only appends, which matches normal TUI session behavior.","next_action_hint":"Look for remaining steady-state cost in renderMessageCached key construction and output slice/anchor allocation, or add a second benchmark for incremental append behavior."}}
~ autoresearch.md
@@ -31,3 +31,4 @@ Reduce the steady-state cost of `internal/tui.View.BuildWithAnchors` on a large,
31 - Established a benchmark that renders a large synthetic transcript with assistant markdown, tool calls, and tool results so repeated `BuildWithAnchors` calls exercise the cache and transcript bookkeeping hot path. 32 - Cached transcript-derived metadata in `View`: tool-call path/label maps, per-message hashes, and assistant `turnOpen` state are now built incrementally as `Messages` grows instead of recomputed on every redraw. 33 - Fixed `autoresearch.sh` metric extraction so `go test -benchmem` columns are parsed into numeric `ns/op`, `B/op`, and `allocs/op` medians. 34+- Profiling showed `hashMessage` still dominated CPU after the first cache pass because `BuildWithAnchors` eagerly recomputed a hash before checking the cached slice; deferring the fallback hash path cut steady-state render cost by another order of magnitude.
~ internal/tui/view.go
@@ -215,9 +215,11 @@ func (v *View) BuildWithAnchors(width int) ([]string, []MessageAnchor) {
215 rendered := make([][]string, len(v.Messages)) 216 total := 0 217 for idx, m := range v.Messages { 218- h := hashMessage(m) 218+ h := uint64(0) 219 if idx < len(v.messageHashes) { 220 h = v.messageHashes[idx] 221+ } else { 222+ h = hashMessage(m) 223 } 224 turnOpen := idx < len(v.messageTurnOpen) && v.messageTurnOpen[idx] 225 lines := v.renderMessageCached(m, width, turnOpen, h)
+ tui.test
Environment
Archaarch64
OSDarwin 14.6.1
CPUApple M2
Agentpi-autoresearch / pi
Files.community-computer/published.json, autoresearch.jsonl, autoresearch.md, internal/tui/view.go, tui.test