Skip to content

perf(memo): evaluate passthrough bodies once and reuse their analysis - #7123

Open
FarhanAliRaza wants to merge 9 commits into
reflex-dev:mainfrom
FarhanAliRaza:farhan/memo-body-analysis
Open

FarhanAliRaza wants to merge 9 commits into
reflex-dev:mainfrom
FarhanAliRaza:farhan/memo-body-analysis

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Third of three stacked compile-performance PRs. Stacked on #7121 and #7122; this diff includes both. Merge those first.

  • create_passthrough_component_memo builds the passthrough definition directly from the fixed children signature (_PASSTHROUGH_PARAMS) and evaluates the body once. Previously it evaluated once to derive the tag and again inside _create_component_definition.
  • component_hash retains the rendered body and the artifacts it already collected (imports, internal and added hooks, custom code, dynamic import, app wraps) in a _MemoBodyAnalysis on the registration context, keyed by content hash. The component records its key in _memo_analysis_key (cleared with the other compile caches).
  • compile_experimental_component_memo reuses that analysis when the styled root still matches (_MemoBodyAnalysis.can_reuse: same class, default __copy__, equal style and style metadata) instead of re-rendering and re-collecting. Older reflex-base releases without the analysis fall back to the previous path.
  • Adds _repeated_stateful_page to the benchmark fixtures.

Measurements

Docs site dry compile (511 routes), warm runs:

state compile
main 46.5 to 47.1 s
after #7121 + #7122 39.3 to 39.6 s
this PR 37.2 to 37.4 s

cProfile: create_passthrough_component_memo -7.5 s, compile_experimental_component_memo -2.0 s, _evaluate_memo_function calls 21.5k to 10.8k (profiled time; the full stack goes 112.5 s to 78.9 s profiled).

Test plan

  • New tests in tests/units/reflex_base/components/test_memo.py: analysis recorded and reused, invalidated on style change, isolated per registration context fork.
  • tests/units/reflex_base, tests/units/components, tests/units/compiler, benchmark compile tests green apart from failures that reproduce on clean main here.
  • pyi_hashes.json regenerated; ruff, pyright, pre-commit clean.

https://claude.ai/code/session_01PmizE1eQhtYZyVs1RK2ke3

Review in cubic

@FarhanAliRaza
FarhanAliRaza requested a review from a team as a code owner September 11, 2026 23:14
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
πŸ“ Code Review βœ… Completed 2026-09-11T23:19:29.292461Z 35c11e1 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with πŸ‘€ while any review is running, comments if it has suggestions, and reacts with πŸ‘ once all reviews finish with no findings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 21 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/event/__init__.py Outdated
Comment thread packages/reflex-base/src/reflex_base/components/memo.py
Comment thread packages/reflex-base/src/reflex_base/vars/base.py
Comment thread tests/units/reflex_base/components/test_memo.py Outdated
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with no outstanding correctness, security, or repository-rule findings.

Summary

This stacked performance change reduces repeated work throughout component compilation.

  • Evaluates generated passthrough memo bodies once and reuses retained render and dependency analysis when the styled root remains compatible.
  • Interns event chains and memoized event wrappers within each registration context.
  • Optimizes component prop traversal, plain Tag rendering, literal-Var dispatch, app-wrap comparison, and cached-property keys.
  • Adds focused regression and benchmark coverage for reuse, invalidation, context isolation, rendering compatibility, and repeated stateful components.

Reviews (6) Β· Last reviewed commit: "chore: regenerate pyi hashes after rebas..."

Comment thread reflex/compiler/utils.py
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/memo-body-analysis branch from c5203e8 to 980db27 Compare September 15, 2026 13:27

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread tests/units/test_event.py
Component render, Var collection, and the prop-component scan walked every
declared prop through the field descriptor to find the few that are set.
Iterate the instance dict plus class-level defaults instead. Cache the
literal Var class per exact value type, short-circuit app-wrap dedupe on
identity, skip the generic tag protocol for plain tags, and hoist the
memoize plugin's component imports.

Docs site dry compile (511 pages): 47 s to 40 s.

Claude-Session: https://claude.ai/code/session_01PmizE1eQhtYZyVs1RK2ke3
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/memo-body-analysis branch from 980db27 to 381ae9f Compare September 15, 2026 13:46
EventChain.create rebuilt an identical chain for every component that bound
the same handler to the same trigger, and the memoize pass then rendered
each chain again to name its useCallback wrapper. Intern the chain on the
handler keyed by args spec and trigger, and key the wrapper cache by chain
identity so repeated call sites reuse the wrapper without rendering.

Claude-Session: https://claude.ai/code/session_01PmizE1eQhtYZyVs1RK2ke3
Deep-copying a component walked into the handler's chain cache and copied
every chain bound to it. The cache now lives on the RegistrationContext,
keyed by handler, args spec and trigger, so handlers carry no state and a
forked context starts with its own chains.
Those handlers are fresh copies at every call site, so a cached entry can
never be hit again and would only retain the copy. Also add the root news
fragment.
Passthrough memo wrappers evaluated the wrapped body twice to derive the
tag, and module emission rendered it a third time to collect hooks, imports,
custom code, and dynamic imports. Build the passthrough definition directly
from the fixed children signature, retain the rendered body and its
artifacts keyed by content hash, and let emission reuse them when the styled
root still matches.

Claude-Session: https://claude.ai/code/session_01PmizE1eQhtYZyVs1RK2ke3
Comment thread packages/reflex-base/src/reflex_base/components/component.py
@FarhanAliRaza
FarhanAliRaza force-pushed the farhan/memo-body-analysis branch from 381ae9f to 17cc398 Compare September 15, 2026 13:49
@codspeed

codspeed Bot commented Sep 15, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚑ 11 improved benchmarks
❌ 1 regressed benchmark
βœ… 28 untouched benchmarks
πŸ†• 7 new benchmarks
⏩ 8 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_from_event_type[event_handler] 184.4 ¡s 191.6 ¡s -3.74%
⚑ test_compile_page_full_context[_stateful_page] 78.4 ms 51.6 ms +52.08%
⚑ test_compile_page[_stateful_page] 69 ms 47 ms +46.73%
⚑ test_compile_all_artifacts[_stateful_page] 60.7 ms 41.5 ms +46.34%
⚑ test_compile_page[_complicated_page] 275 ms 211.8 ms +29.85%
⚑ test_compile_page_full_context[_complicated_page] 320.4 ms 251.4 ms +27.47%
⚑ test_compile_all_artifacts[_complicated_page] 154 ms 132.5 ms +16.25%
⚑ test_evaluate_page_with_hooks[_complicated_page] 62.7 ms 57.9 ms +8.3%
⚑ test_evaluate_page[_complicated_page] 62.2 ms 57.5 ms +8.21%
⚑ test_console_log 815.1 ¡s 770.1 ¡s +5.83%
⚑ test_evaluate_page[_stateful_page] 10.5 ms 9.9 ms +5.48%
⚑ test_evaluate_page_with_hooks[_stateful_page] 11 ms 10.5 ms +5.1%
πŸ†• test_collect_imports[_repeated_stateful_page] N/A 5.9 ms N/A
πŸ†• test_compile_all_artifacts[_repeated_stateful_page] N/A 466.8 ms N/A
πŸ†• test_compile_page_full_context[_repeated_stateful_page] N/A 571 ms N/A
πŸ†• test_compile_page[_repeated_stateful_page] N/A 549.4 ms N/A
πŸ†• test_get_all_imports[_repeated_stateful_page] N/A 23.8 ms N/A
πŸ†• test_evaluate_page_with_hooks[_repeated_stateful_page] N/A 56.6 ms N/A
πŸ†• test_evaluate_page[_repeated_stateful_page] N/A 56.1 ms N/A

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing FarhanAliRaza:farhan/memo-body-analysis (4313e7c) with main (2f63cb3)

Open in CodSpeed

Footnotes

  1. 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. ↩

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