Skip to content

perf: lazy constant resolution in ErgoTree evaluation#858

Open
mwaddip wants to merge 4 commits into
ergoplatform:developfrom
mwaddip:feature/lazy-const-resolution
Open

perf: lazy constant resolution in ErgoTree evaluation#858
mwaddip wants to merge 4 commits into
ergoplatform:developfrom
mwaddip:feature/lazy-const-resolution

Conversation

@mwaddip
Copy link
Copy Markdown

@mwaddip mwaddip commented Apr 12, 2026

Resolve ConstPlaceholder from the eval context on demand instead of deep-cloning the AST via proposition() each reduce_to_crypto call. Mainnet-sync profiling in ergo-node-rust showed the per-input clone-and-substitute was 8-12% of total CPU.

Adds root_expr() / constants() accessors and Context::with_constants(). Deserialize trees fall back to proposition() (incompatible with lazy resolution). proposition() itself unchanged.

Beware of conflicts with #854, see comments below

mwaddip and others added 4 commits April 12, 2026 21:39
…tion

Add Option<&'ctx [Constant]> to Context<'ctx> with a with_constants()
method that clones the context with a constants slice set. This enables
the evaluator to resolve ConstPlaceholder nodes on-demand without
cloning the entire ErgoTree AST.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These return references into the parsed tree without cloning,
enabling the evaluator to work directly with the stored AST.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of erroring on ConstPlaceholder, look up the constant by id
from ctx.constants. This enables evaluation without the upfront clone
and tree-walk that substitute_constants() performs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For the common case (no deserialize nodes), reduce_to_crypto now
borrows the root Expr and constants slice directly from the ErgoTree
instead of cloning the entire AST and walking it to substitute
ConstPlaceholder nodes.

ConstPlaceholder nodes are resolved on-demand during evaluation by
indexing into ctx.constants. The deserialize path (rare) retains the
existing proposition() clone for substitute_deserialize compatibility.

Diagnostic paths (false-reduction, eval errors) fall back to
proposition() for fully-resolved pretty-printing — these are rare
and don't affect the hot path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mwaddip
Copy link
Copy Markdown
Author

mwaddip commented Apr 21, 2026

Rebase guidance update (re: #876 — "JIT Costing: The Definitive PR")

Update: the JIT-costing work was re-sliced into #876 (the "Definitive" PR), which
supersedes #854. The reconciliation below is unchanged in substance — #876 carries the
same proposition_for_cost_eval + ConstantPlaceholder::resolved machinery and already
charges ConstPlaceholder at JitCost(1).

#876 and this PR overlap in reduce_to_crypto / ConstPlaceholder: #876 resolves segregated
placeholders via ConstantPlaceholder::resolved (populated by proposition_for_cost_eval);
this PR resolves them via Context.constants. The ergo-node-integration branch carries both
layered — see 9094f693 for the worked reconciliation.

If #876 merges first → this PR rebases:

  • Add constants: Option<&'ctx [Constant]> to Context alongside the jit_cost fields.
  • reduce_to_crypto: keep the deserialize path; refactor the common path to tree.root_expr()
    • with_constants(). Preserve cumulative-cost tracking by syncing the cloned Cell back after
      inner returns (ctx.jit_cost.set(ctx_with_c.jit_cost_value())).
  • Expr::ConstPlaceholder eval arm: resolve via ctx.constants (cost stays JitCost(1)).
  • JIT Costing: The Definitive PR #876's ConstantPlaceholder::resolved field becomes redundant — drop it (or keep as no-op).
  • trivial_reduce: resolve segregated placeholders via ctx.constants on the common path.

If this PR merges first → #876 rebases: add jit_cost / jit_cost_limit alongside
constants; add the cost_before snapshot + post-inner sync on the common path; drop the
resolved field; switch the ConstPlaceholder arm to resolve via ctx.constants (cost already
1); retune the placeholder/limit tests.

Net: whichever merges first, the ConstPlaceholder arm ends up charging 1 JitCost and reading
from ctx.constants. The rest is plumbing.

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