perf(memo): evaluate passthrough bodies once and reuse their analysis - #7123
FarhanAliRaza wants to merge 9 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
βΉοΈ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with π while any review is running, comments if it has suggestions, and reacts with π once all reviews finish with no findings. |
There was a problem hiding this comment.
All reported issues were addressed across 21 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
c5203e8 to
980db27
Compare
There was a problem hiding this comment.
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
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
980db27 to
381ae9f
Compare
β¦d the root news fragment
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
381ae9f to
17cc398
Compare
Merging this PR will regress 1 benchmark
|
Summary
Third of three stacked compile-performance PRs. Stacked on #7121 and #7122; this diff includes both. Merge those first.
create_passthrough_component_memobuilds the passthrough definition directly from the fixedchildrensignature (_PASSTHROUGH_PARAMS) and evaluates the body once. Previously it evaluated once to derive the tag and again inside_create_component_definition.component_hashretains the rendered body and the artifacts it already collected (imports, internal and added hooks, custom code, dynamic import, app wraps) in a_MemoBodyAnalysison 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_memoreuses 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._repeated_stateful_pageto the benchmark fixtures.Measurements
Docs site dry compile (511 routes), warm runs:
cProfile:
create_passthrough_component_memo-7.5 s,compile_experimental_component_memo-2.0 s,_evaluate_memo_functioncalls 21.5k to 10.8k (profiled time; the full stack goes 112.5 s to 78.9 s profiled).Test plan
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 cleanmainhere.pyi_hashes.jsonregenerated; ruff, pyright, pre-commit clean.https://claude.ai/code/session_01PmizE1eQhtYZyVs1RK2ke3