Dedup auto-memoize passthrough definitions during compile#6735
Conversation
The auto-memoize plugin built a full memo definition for every stateful call site and only deduped by tag afterwards. Per component this ran _analyze_params twice on a constant signature (get_type_hints eval-compiles the stringized annotations each time) and evaluated the memo body twice. - Hoist the passthrough params analysis to an import-time constant; the wrapper signature (children: Var[Component]) never changes. - Reuse the evaluated preview as the definition body instead of re-running the memo function inside _create_component_definition. - Check CompileContext.auto_memo_components for the computed (tag, source_module) key before building, so repeated identical subtrees reuse the first definition instead of rebuilding an equivalent one per call site. Adds a duplicated-rows compile benchmark that stresses this path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
Greptile SummaryThis PR speeds up auto-memoized passthrough component compilation. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (5): Last reviewed commit: "perf(memoize): compute memo tag up front..." | Re-trigger Greptile |
Merging this PR will improve performance by 29.41%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_compile_page[_stateful_page] |
30.8 ms | 23.1 ms | +33.2% |
| ⚡ | test_compile_all_artifacts[_stateful_page] |
27.2 ms | 21.3 ms | +27.79% |
| ⚡ | test_compile_page_full_context[_stateful_page] |
35.8 ms | 28.1 ms | +27.31% |
| 🆕 | test_compile_page_duplicated_rows |
N/A | 646.9 ms | N/A |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/reflex-compiler-perf-t8ztc9-11-memoize-dedup (0390d78) with main (9a5c4d3)
Footnotes
-
8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
create_passthrough_component_memo gained the existing_definitions parameter, changing the generated stub. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
…build
Hash the call-site component with a constant {children}-hole render to
derive the memo tag before any body work happens. On a registry hit the
plugin now skips the event-trigger rewrite and the memo body build
entirely; on a miss the new prepare_body hook applies the rewrite and
the definition self-registers.
Soundness of dedup-by-tag requires the component hash to cover every
input the memo module emission reads, so dynamic imports are now hashed
(with a regression test). The hash walk also gets exact-type fast paths
for str/dict/list and a per-class dataclass field-name cache.
All Submissions:
Type of change
Changes To Core Features:
What this does
The auto-memoize plugin (
MemoizeStatefulPlugin) built a full memo definition for every stateful call site and only deduped by(tag, source_module)afterwards. In profiling of a 500-duplicated-row page this was ~56% of compile CPU: 1,001 definitions were built and then collapsed to 3 unique ones. Per call site it:_analyze_paramstwice on a constant signature — withfrom __future__ import annotations,get_type_hintseval-compiles the stringized annotations each time (2,472builtins.compilecalls in the profile), and_create_component_definition).Fix (behavior-preserving)
_PASSTHROUGH_PARAMS): the passthrough wrapper signature is always(children: Var[Component]) -> Component._LazyBody.ready(_lift_rest_props(preview))) instead of re-running the memo function.CompileContext.auto_memo_componentsfor the computed(tag, source_module)key first (new optionalexisting_definitionsparameter): repeated identical subtrees reuse the first definition instead of rebuilding an equivalent one per call site. The cache stays per-CompileContext, so definitions are still never shared across compiles (covered bytest_passthrough_memo_definitions_are_not_shared_globally).Compile output is unchanged: the surviving definition is structurally identical to the one that previously overwrote the registry entry last.
Measured results (CodSpeed instrumentation, this PR vs
main@ 9a5c4d3)Overall: +21.96% compile performance.
BASEHEADtest_compile_all_artifacts[_stateful_page]test_compile_page[_stateful_page]test_compile_page_full_context[_stateful_page]test_compile_page_duplicated_rows(new)Verification
source_modulestill misses); the memo body is evaluated exactly once per definition build.test_compile_page_duplicated_rows(200 identical stateful rows) stresses this path so follow-up compiler-perf PRs are measured on every run.Part of a compiler-performance series; tracked in Linear as ENG-10101.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g