Skip to content

perf(apiStabilityLeak): reuse one analysis per file - #870

Merged
mattiamanzati merged 1 commit into
mainfrom
t3/reuse-api-stability-session
Oct 16, 2026
Merged

mattiamanzati merged 1 commit into
mainfrom
t3/reuse-api-stability-session

Conversation

@mattiamanzati

@mattiamanzati mattiamanzati commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Why

apiStabilityLeak built a fresh ApiStabilitySession for every export of a file, and each one allocated and regrew its maps and work queues. On packages/effect the rule allocated about 250 MB this way. The rule now keeps one session per file and resets it before each export.

What changed

  • ApiStabilitySession.Reset calls a new apiStabilityAnalysis.reset. That clears every per-export map, truncates the settle and publish slices, resets the settle scope and zeroes the budgets and the materialization epoch. The allocated capacity is kept.
  • A shared-cache hit in typeSurface and signatureSurface goes into the session map and the settle order, but not into the publication queue. Publication skipped it anyway.
  • publishTypeSurface and publishSignatureSurface allocate a snapshot's findings map only for the first retained finding.

Scope

The settle scoping and the lazy surface findings maps from #862 and #863 are unchanged. A future per-export field on apiStabilityAnalysis must also be cleared in reset. The new test compares a reset analysis field by field with a fresh one, and it fails on a field kind it does not know.

Blast radius

Only apiStabilityLeak uses the session, and the rule is off by default. A missed field in reset would leak one export's results into the next export. The field-by-field test and the byte-identical diagnostic dump guard against that.

Verification

  • Effect 4.0.5 (7f524e25) with every rule executing, packages/effect: checkApiStabilityLeaks allocation 252 MB → 140 MB (median of 5 interleaved profiled runs per side, ranges disjoint). Rule CPU moved 0.98 s → 0.92 s, inside the noise.
  • A dump of every rule-returned diagnostic from a clean build of the whole workload is byte-identical on both sides, with 1,404 apiStabilityLeak diagnostics each.
  • New tests in api_stability_reset_test.go check three things: a reset analysis equals a fresh one, a stale budget or result does not reach the next export, and a cache hit is not queued for publication again. Mutants that drop a clear or the work reset, or that re-queue the hit, all fail them. go test ./internal/..., pnpm lint and pnpm check pass. pnpm test did not run.
  • Unprofiled clean builds of the whole workload, 8 interleaved pairs: median aggregate check time is 63.4 s before and 63.2 s after, with patched faster in 4 of 8 pairs. Wall time shows no measurable change on a shared, loaded machine.

🤖 Generated with Claude Code

checkApiStabilityLeaks allocated a fresh ApiStabilitySession for every
export, and each one built and regrew its maps and work queues. The rule
now keeps one session per file and calls Reset before each export.
apiStabilityAnalysis.reset clears every per-export map, truncates the
settle and publish slices, resets the settle scope and zeroes the
budgets and the materialization epoch, keeping the allocated capacity.

A shared-cache hit in typeSurface and signatureSurface is recorded in the
session map and the settle order without being queued for publication,
since publication would skip it anyway. publishTypeSurface and
publishSignatureSurface allocate the snapshot's findings map only for
the first retained finding.

Effect 4.0.5 workload (7f524e25), every rule executing, packages/effect,
5 interleaved profiled runs per side, medians:
  checkApiStabilityLeaks alloc_space  252 MB -> 140 MB
  rulerunner.Run alloc_space          754 MB -> 633 MB
  checkApiStabilityLeaks CPU          0.98 s -> 0.92 s (ranges overlap)

Diagnostic equivalence: a dump of every rule-returned diagnostic from a
clean build of the whole workload is byte-identical on both sides
(1,404 apiStabilityLeak diagnostics on each).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Effect perf vs stock tsgo: 2 flagged rows

  • latest · Memory allocs over stock · 🟩 -1.2 pts
  • latest · Check time over stock · 🟩 -3.5 pts
Details
MetricbaseprDeltaBestWorstp-value
latest · 7.0.2 · effect (all rules)
Errors11~11counter, n=5
Warnings00~00counter, n=5
Symbols over stock+712,107 (±4)+712,113 (±4)~+712,106+712,113counter, n=5
Types over stock+340,880 (±5)+340,884 (±2)~+340,882+340,886counter, n=5
Instantiations over stock+1,208,629 (±11)+1,208,645 (±11)~+1,208,626+1,208,648counter, n=5
Memory allocs over stock+19.3% (19.2 to 19.6)+18.1% (17.8 to 18.2)🟩 -1.2 pts+17.8%+18.2%p=0.008 n=5
Check time over stock+41.2% (39.8 to 42.6)+37.7% (33.1 to 39.6)🟩 -3.5 pts+33.1%+39.6%p=0.008 n=5
Total time over stock+27.2% (25.6 to 29.3)+22.8% (21.0 to 27.7)~+21.0%+27.7%p=0.095 n=5
next · 7.1.0-dev.20261015.1 · effect (all rules)
Errors1818~1818counter, n=5
Warnings00~00counter, n=5
Symbols over stock+564,074+564,074 (±3)~+564,068+564,074counter, n=5
Types over stock+299,171 (±1)+299,169 (±3)~+299,165+299,171counter, n=5
Instantiations over stock+1,001,074 (±2)+1,001,071 (±10)~+1,001,055+1,001,074counter, n=5
Memory allocs over stock+16.3% (15.8 to 17.9)+15.8% (15.0 to 16.5)~+15.0%+16.5%p=0.222 n=5
Check time over stock+36.1% (33.4 to 39.7)+37.7% (27.6 to 43.0)~+27.6%+43.0%p=0.690 n=5
Total time over stock+26.0% (22.3 to 27.4)+27.0% (22.8 to 29.3)~+22.8%+29.3%p=0.421 n=5
  • Stock counters, latest: 7.0.2 symbols 18,255,289 (±1) · types 6,184,373 · instantiations 26,135,429
  • Stock counters, next: 7.1.0-dev.20261015.1 symbols 17,710,777 · types 5,960,725 · instantiations 25,181,429
  • Workload: https://github.com/Effect-TS/effect@7f524e25cfb49a71ac39b0f44278d08d40dda5de with every Effect rule executing (skipDisabledOptimization: true)
  • Pairs per side: 5, alternating stock-first and patched-first
  • Commits: base d3b53a718365b55cc11799d08e67e54869714890 · head 73dfcbfe632fa7af152e4b8151e10937100a0319
  • Counters are patched − stock per pair (median ±half-range); allocs and times are patched / stock − 1 per pair, median (min to max). Time and alloc deltas are flagged only when the exact two-sided Mann-Whitney U p < 0.01.

@mattiamanzati
mattiamanzati merged commit b5df4e9 into main Oct 16, 2026
15 checks passed
@mattiamanzati
mattiamanzati deleted the t3/reuse-api-stability-session branch October 16, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant