Skip to content

Bypass f-string tag round-trip for var operation operands#6798

Open
Alek99 wants to merge 4 commits into
claude/reflex-perf-optimizations-21kg8y-redisfrom
claude/reflex-compiler-perf-t8ztc9-14-format-tag-bypass
Open

Bypass f-string tag round-trip for var operation operands#6798
Alek99 wants to merge 4 commits into
claude/reflex-perf-optimizations-21kg8y-redisfrom
claude/reflex-compiler-perf-t8ztc9-14-format-tag-bypass

Conversation

@Alek99

@Alek99 Alek99 commented Jul 18, 2026

Copy link
Copy Markdown
Member

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Performance improvement + memory-leak fix (non-breaking change, identical rendered output)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?

What this does

Interpolating a Var into an f-string (Var.__format__) hashes the var, permanently registers it in the module-global _global_vars dict (which never evicts — a real memory leak, worst under dev hot-reload since entries and their GLOBAL_CACHE values survive GC), and emits a <reflex-var> tag that the constructed expression's __post_init__ then regex-decodes back out. Profiling showed this round-trip is ~30–40% of var-operation construction — and every @var_operation (all arithmetic, comparisons, string/array/object ops) pays it for each operand.

Fix

Operand VarData already reaches the operation through CustomVarOperation._args, so the tag round-trip is pure overhead for operands. var_operation now suppresses tagging on its operand vars while the body runs:

  • the suppression is a ref-counted instance-dict flag, so a nested operation on the same var can't clear an outer suppression early, and it's always removed in a finally;
  • vars created inside an operation body are not operands and still tag normally, so their VarData keeps flowing through the return expression (covered by a dedicated test);
  • formatting outside an operation is unchanged.

Net effect: constructing a + b no longer hashes/registers/regex-decodes its operands, and internal ops no longer grow _global_vars.

Verification

  • New unit tests: _global_vars does not grow when constructing an operation while operand VarData still reaches the merged result; the suppression flag does not persist after the op; body-created vars keep contributing VarData; formatting outside an operation still registers/tags as before.
  • CodSpeed instrumentation on this PR is the authoritative before/after CPU measurement (this sandbox has no PyPI egress for local profiling); numbers will be posted in this description once the run completes — the affected path is exercised by test_console_log, test_evaluate_page*, and all test_compile_* benchmarks.

Part of a compiler-performance series; tracked in Linear as ENG-10104.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g


Generated by Claude Code

claude and others added 4 commits July 17, 2026 18:44
Interpolating a var into an f-string hashes it, permanently registers
it in the module-global _global_vars dict (which never evicts - a
memory leak, worst under dev hot-reload), and emits a tag that the
return expression's __post_init__ regex-decodes back out. For operands
of a @var_operation this round-trip is pure overhead (~30-40% of op
construction): the operation merges operand VarData directly from
_args.

var_operation now suppresses tagging on its operands (ref-counted, so
nested operations on the same var can't clear an outer suppression
early) while the body runs. Vars created inside the body still tag
normally, so their VarData keeps flowing through the return
expression.

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
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@Alek99
Alek99 requested a review from a team as a code owner July 18, 2026 02:22
@codspeed-hq

codspeed-hq Bot commented Jul 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 27 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing claude/reflex-compiler-perf-t8ztc9-14-format-tag-bypass (5f75b20) with claude/reflex-perf-optimizations-21kg8y-redis (4c6cc4f)

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.

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR optimizes @var_operation construction by suppressing the f-string tag round-trip (hash → _global_vars registration → regex decode) for operand vars, since their VarData already reaches the merged result through CustomVarOperation._args. A ref-counted instance-dict flag _format_without_tagging is set on each operand before the body runs and cleaned up in a finally block.

  • __format__ short-circuit: when _format_without_tagging is truthy, the method returns str(self) directly, skipping hashing and _global_vars registration for operand vars inside an operation body.
  • Ref-counted suppression in var_operation wrapper: all operand vars are incremented before the body executes and decremented in finally, so nested operations on the same var interact correctly; vars created inside the body are not in _args and still flow through the tag mechanism.
  • New tests: verify _global_vars growth is suppressed, the flag is cleaned up, operand VarData still reaches the result, and body-created vars still contribute their VarData.

Confidence Score: 3/5

Safe in all normal usage paths; there is an error-path correctness issue where the increment loop running outside the try block can permanently leave _format_without_tagging set on operands when iteration is interrupted mid-way.

The optimization is logically correct and well-tested for the happy path. The concern is that the for-loop incrementing _format_without_tagging runs before the try block — if it raises mid-iteration (e.g., LiteralVar.create returns None for a range argument, placing None in operands, causing AttributeError on None.dict), any already-incremented vars have the flag permanently stuck, silently disabling tagging for those vars in all future uses.

packages/reflex-base/src/reflex_base/vars/base.py — specifically the operand increment loop in the var_operation wrapper and the try/finally structure around it.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/vars/base.py Adds ref-counted _format_without_tagging flag to bypass f-string tagging for var_operation operands; increment loop sits outside the try-finally, which can permanently poison operands if it raises mid-iteration.
tests/units/vars/test_base.py Adds four targeted tests covering: no _global_vars growth during operations, flag cleanup after op, operand VarData still reachable, and body-created vars still flowing VarData through the return expression.
packages/reflex-base/news/6747.performance.md Changelog entry describing the performance improvement; no issues.

Reviews (1): Last reviewed commit: "Appease pyright on operand __dict__ tag ..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f75b20c48

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +937 to +938
if self.__dict__.get("_format_without_tagging"):
return str(self)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep format suppression local to the operation context

When the same Var is shared across threads, this instance-dict flag changes __format__ process-wide while an operation body is running. A concurrent f"{var}" therefore returns the raw expression without its tag, so a Var constructed from that string loses the original imports/hooks; overlapping operations can also race the non-atomic reference-count updates and leave the flag set or raise during cleanup. Use thread/task-local suppression rather than mutating the shared immutable operand.

Useful? React with 👍 / 👎.

Comment on lines +1959 to +1973
for operand in operands:
operand_dict = operand.__dict__
operand_dict["_format_without_tagging"] = ( # pyright: ignore[reportIndexIssue]
operand_dict.get("_format_without_tagging", 0) + 1
)
try:
return_var = func(*args_vars.values(), **kwargs_vars) # pyright: ignore [reportCallIssue]
finally:
for operand in operands:
operand_dict = operand.__dict__
remaining = operand_dict["_format_without_tagging"] - 1
if remaining:
operand_dict["_format_without_tagging"] = remaining # pyright: ignore[reportIndexIssue]
else:
del operand_dict["_format_without_tagging"] # pyright: ignore[reportIndexIssue]

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.

P1 Increment loop outside try-finally can permanently poison operands

The increment loop runs before the try block, so if it raises mid-iteration (e.g., an operand whose __dict__ is inaccessible), any operands already incremented will have _format_without_tagging stuck at a non-zero value permanently — causing them to silently bypass tagging in all future uses. This can happen today when LiteralVar.create(range_value) returns None (see LiteralVar.create line ~1802), placing None in operands; the first valid-var operand gets incremented, then None.__dict__ raises before the try, so the finally never runs. Moving the increment loop inside try (and using .get(..., 0) in the decrement to tolerate partial increments) would guarantee cleanup regardless of which step fails.

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.

3 participants