Skip to content

Reuse exact positional arguments as function frame locals - #701

Open
KRRT7 wants to merge 12 commits into
pydantic:mainfrom
KRRT7:exact-positional-call-fast-path
Open

Reuse exact positional arguments as function frame locals#701
KRRT7 wants to merge 12 commits into
pydantic:mainfrom
KRRT7:exact-positional-call-fast-path

Conversation

@KRRT7

@KRRT7 KRRT7 commented Aug 10, 2026

Copy link
Copy Markdown

Summary

Optimize direct synchronous Python function calls when the supplied positional arguments exactly match a simple function signature.

Before this change, the VM:

  1. removed arguments from the operand stack;
  2. wrapped them in ArgValues;
  3. ran signature binding;
  4. built locals in the namespace scratch buffer; and
  5. copied those locals back onto the operand stack.

For eligible calls, the stack already contains:

[... caller values, callable, arg0, arg1, ...]

Removing the immediate DefFunction shifts its arguments directly into their final parameter slots. The VM then extends that region for remaining locals and pushes the frame without argument wrappers, binding, scratch storage, or another stack copy.

The fast path is limited to synchronous direct functions with:

  • an exact positional argument count;
  • no defaults;
  • no *args, keyword-only arguments, or **kwargs;
  • no owned or captured closure cells.

All other calls retain the existing binding path.

Performance

Local Criterion benchmark for recursive fib(25):

before: 18.74 ms
after:  14.70 ms
change: -21.4%

Criterion reported a statistically significant improvement:

change: [-22.215% -21.437% -20.836%] (p = 0.00)

Validation

  • cargo test -p monty
  • all function__* Monty/CPython test cases: 74 passed
  • function__arity_defaults with memory-model-checks
  • recursion-depth and recursion-traceback tests with memory-model-checks
  • cargo clippy -p monty --lib -- -D warnings
  • formatting check
  • git diff --check

Summary by cubic

Speeds up direct calls by reusing stack arguments as frame locals when positional args exactly match the signature, for direct DefFunction calls in sync funcs and async coroutines. Local fib(25): 18.74 ms → 14.70 ms (−21.4%).

  • New Features

    • Lazy exact-call caches via OnceCell for Signature bind mode and Function call plans; computed on first use, not serialized.
    • Async fast path: create the coroutine by moving args directly into its namespace; functions with cells still use the general path.
  • Bug Fixes

    • Validate function metadata on REPL dump load and reject malformed dumps (frame/namespace limits, signature slots in range, closure vector length/order/range, no slot overlap, defaults count, cell param indices); dump format bumped to v6.
    • Support functions with 65+ parameters by tracking bound params via Undefined slots instead of a 64-bit bitmap.

Written for commit c772a86. Summary will update on new commits.

Review in cubic

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.61905% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/monty/src/function.rs 97.10% 3 Missing and 1 partial ⚠️
crates/monty/src/args/bind_python.rs 97.70% 1 Missing and 1 partial ⚠️
crates/monty/src/intern.rs 87.50% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@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.

No issues found across 2 files

Re-trigger cubic

@codspeed-hq

codspeed-hq Bot commented Aug 10, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 19.65%

⚠️ 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

⚡ 1 improved benchmark
✅ 35 untouched benchmarks
⏩ 16 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
fib__monty 156.9 ms 131.1 ms +19.65%

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 KRRT7:exact-positional-call-fast-path (c772a86) with main (2680c32)

Open in CodSpeed

Footnotes

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

@samuelcolvin samuelcolvin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the principle looks good, is it fully tested?

Comment thread crates/monty/src/args/bind_python.rs Outdated
Comment thread crates/monty/src/bytecode/vm/call.rs Outdated

@rewitt94 rewitt94 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.

Thanks for contributing again!

Comment thread crates/monty/src/bytecode/vm/call.rs
@KRRT7

KRRT7 commented Aug 11, 2026

Copy link
Copy Markdown
Author

the principle looks good, is it fully tested?

I tested it on a few scenarios with no issues.

KRRT7 and others added 5 commits August 11, 2026 13:33
Switch Function::exact_positional_call and Signature::bind_mode to
lazily-derived OnceCell fields instead of serde-skip-plus-rebuild, so
a value loaded from an older REPL dump can never be stale relative to
the derivation logic in the loading binary. Also dedupes the fast
path's namespace-padding and callable-identity checks, and tightens
the exact-call test coverage (arg-count-guard regression test, and
exception-message assertions on the new arity-mismatch tests).

@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 7 files (changes from recent commits).

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

Re-trigger cubic

Comment thread crates/monty/src/args/bind_python.rs
Comment thread crates/monty/src/bytecode/vm/call.rs
Comment thread crates/monty/src/args/bind_python.rs Outdated
KRRT7 and others added 2 commits August 11, 2026 15:38
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Comment thread crates/monty/src/function.rs
@veria-ai

veria-ai Bot commented Aug 11, 2026

Copy link
Copy Markdown

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@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 crates/monty/src/args/bind_python.rs Outdated

@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 crates/monty/src/function.rs Outdated
Comment thread crates/monty/src/function.rs Outdated
Comment thread crates/monty/src/function.rs Outdated
Comment thread crates/monty/tests/repl.rs Outdated

@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 5 files (changes from recent commits).

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

Re-trigger cubic

Comment thread crates/monty/src/function.rs
Comment thread crates/monty/src/function.rs
Comment thread crates/monty/src/function.rs Outdated
@KRRT7

KRRT7 commented Aug 11, 2026

Copy link
Copy Markdown
Author

While doing the final review, I found an adjacent, pre-existing argument-binding bug. I verified it directly on main at the PR’s merge-base commit (2680c325), so it was not introduced by the exact-positional fast path.

Monty supports functions with more than 64 named parameters, but the binder tracked bound parameters in a u64 bitmap. At indexes above 63, debug builds panic with a shift overflow, while release builds alias bitmap bits and can produce incorrect argument errors. The same valid call succeeds on CPython.

Commit c772a863 fixes this by using the namespace’s existing internal Undefined sentinel to track whether each parameter is bound. This removes the fixed-width limit without adding allocations. Regression tests cover positional and keyword binding, defaults, duplicate arguments, and missing arguments beyond 64 parameters.

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