Skip to content

Improving the extractor - #956

Draft
yihozhang wants to merge 4 commits into
mainfrom
perf-extractor-improvements
Draft

Improving the extractor#956
yihozhang wants to merge 4 commits into
mainfrom
perf-extractor-improvements

Conversation

@yihozhang

Copy link
Copy Markdown
Collaborator

The extractor PR bundles four performance optimizations to the extractor's Bellman-Ford cost computation, all guaranteed to leave extraction results byte-identical:

  1. Materialize rows up front — scan each function's rows once per extractor build instead of every pass, and replace string-name map lookups with dense sort ids.
  2. Skip converged functions — version-stamp each eq sort on cost updates and skip relaxing any function whose dependency sorts haven't changed since its last visit.
  3. Semi-naive dirty-row tracking — refine that to row granularity: a reverse index from each (sort, value) to the rows that read it, so a cost update only re-relaxes the affected rows.
  4. Cache the extractor across extract commands — store the extractor on the EGraph keyed by root sort and a fingerprint of backend table versions, so repeated extracts with no intervening changes don't rebuild cost tables.

In short: build cheaper (1), relax less (2, 3), and rebuild less often (4).

yihozhang and others added 4 commits July 20, 2026 00:26
…rd loop

The extractor re-scanned every reachable function table on each
Bellman-Ford pass through the bridge's for_each machinery, and looked up
per-sort cost/topo maps by string name for every child of every row.
Materialize each function's non-subsumed rows once per extractor build,
resolve each child column's handling (eq/container/base) up front, and
index the per-sort maps by a dense id. Relaxation order is unchanged, so
extraction results are identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Track a version counter per eq sort, bumped on every cost update, and
snapshot each function's dependency-sort versions just before its rows
are relaxed. A function whose dependency sorts (eq children, plus eq
sorts reachable inside container children) are unchanged since its last
visit recomputes identical costs for every row, so it is skipped without
altering the relaxation trace — topo ranks and extracted terms are
byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the function-level dependency gate with row-level tracking: a
reverse index maps each (sort, value) an extractable row reads — eq
children plus eq values nested inside container children — to the rows
reading it, and each cost update marks only those rows dirty. Sweeps
visit dirty rows in the same (function, row) order as the naive passes,
so the update trace, topo ranks, and extracted terms are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An extract command built a fresh Extractor — recomputing every cost via
Bellman-Ford — even when nothing changed since the previous extract,
which dominates programs that extract many roots after a run. Cache the
extractor's data on the EGraph, keyed by root sort and a fingerprint of
all backend table versions (any merged mutation, including union-find
changes, alters some table's version). The cache deliberately clones
empty so push/pop don't carry cost tables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yihozhang
yihozhang requested a review from saulshanabrook July 20, 2026 00:40
@yihozhang
yihozhang requested a review from a team as a code owner July 20, 2026 00:40
@yihozhang
yihozhang marked this pull request as draft July 20, 2026 00:42
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