Dedup auto-memoize passthrough definitions during compile - #6735
Conversation
Greptile SummaryThis PR speeds up auto-memoized passthrough component compilation. The main changes are:
Confidence Score: 5/5This looks safe to merge.
|
| Filename | Overview |
|---|---|
| packages/reflex-base/src/reflex_base/components/memo.py | Adds cached passthrough helpers and per-compile memo definition reuse. |
| reflex/compiler/plugins/memoize.py | Moves event-trigger rewriting into the memo body preparation path. |
| packages/reflex-base/src/reflex_base/components/component.py | Updates deterministic component hashing and includes dynamic imports. |
| reflex/compiler/utils.py | Compiles passthrough memo bodies using the stored hole child shape. |
| tests/units/compiler/test_memoize_plugin.py | Adds tests for memo definition reuse, trigger rewriting, and hash coverage. |
| tests/benchmarks/test_compilation.py | Adds a duplicated-rows benchmark for the memo dedup path. |
Reviews (7): Last reviewed commit: "Update moment pyi hash: memo dedup chang..." | Re-trigger Greptile
Merging this PR will improve performance by 28.47%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | test_compile_page[_stateful_page] |
30.4 ms | 23 ms | +31.86% |
| ⚡ | Simulation | test_compile_page_full_context[_stateful_page] |
34.3 ms | 27 ms | +27.36% |
| ⚡ | Simulation | test_compile_all_artifacts[_stateful_page] |
26.8 ms | 21.2 ms | +26.25% |
| 🆕 | Simulation | test_compile_page_duplicated_rows |
N/A | 645.7 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 (460c941) with claude/reflex-perf-optimizations-21kg8y-emit-update (24c3f7a)
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. ↩
0390d78 to
dea5c0c
Compare
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
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.
34610c2 to
24c3f7a
Compare
dea5c0c to
460c941
Compare
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