Skip to content

perf(compiler): drop compile-time inspect.signature from positional fast-path predicate#349

Merged
lesnik512 merged 2 commits into
mainfrom
perf/cold-compile-drop-inspect-signature
Jul 19, 2026
Merged

perf(compiler): drop compile-time inspect.signature from positional fast-path predicate#349
lesnik512 merged 2 commits into
mainfrom
perf/cold-compile-drop-inspect-signature

Conversation

@lesnik512

Copy link
Copy Markdown
Member

What

_positional_names (the compile-time predicate that decides whether a Factory's creator can be called positionally) re-ran a full inspect.signature(creator) on every compile, solely to detect a positional-only parameter that would shift positional binding. The parser already sees that at construction — parse_creator drops the positional-only-with-default param from parsed_kwargs. This records a has_positional_only_gap bool on Factory and reads it instead of re-introspecting.

Behavior is unchanged (same predicate, same four exclusion rules, names-guard preserved so an all-positional-only creator still reaches the fast path). It's a compile-time-only change — no resolve hot-path branch added.

Why

Cold / first-resolve profiling (never benchmarked; matters for serverless cold start, CLIs, and test suites building many fresh containers) found inspect.signature at ~56% of total cold time for a depth-6 chain. Removing it measured 99.8 → 41.8 µs/cold-cycle, 2.4x on clean wall-clock, reconfirmed on the shipped code.

Full rationale in the change file; the profiling that surfaced it in the hotspot-hunt audit, Candidate 1.

Testing

  • just test-ci — 432 passed, 100% line coverage.
  • just lint-ci — ruff/ty/planning all clean.
  • New perf-guard test (test_first_resolve_does_not_reintrospect_creator): red before, green after — a first resolve must not call inspect.signature.
  • Regression: the rule-4 positional-only test and the differential harness stay green.

🤖 Generated with Claude Code

…redicate

_positional_names re-ran a full inspect.signature per compile just to detect a
positional-only parameter that would shift positional binding -- profiled at
~56% of cold first-resolve. The parser already sees positional-only params at
construction (it drops the with-default one from parsed_kwargs); record that as
a has_positional_only_gap flag on Factory and read it instead of re-introspecting.

Behavior is unchanged (same predicate, same four exclusion rules); cold
first-resolve is ~2.4x faster (chain-6 99.8 -> 41.8 us/cold-cycle, wall-clock).

Change: planning/changes/2026-07-19.03-drop-compile-inspect-signature.md
Audit:  planning/audits/2026-07-19-cold-and-cycle-hotspot-hunt-report.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

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

Benchmark

Details
Benchmark suite Current: a55dbb6 Previous: 3fa3428 Ratio
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container 472676.0588164761 iter/sec (stddev: 0.00002815706242293732) 435087.8569622213 iter/sec (stddev: 0.00004635971281596933) 0.92
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope 469463.3636049325 iter/sec (stddev: 0.000009018033698373464) 460050.67072307155 iter/sec (stddev: 0.000009387492478132681) 0.98
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle 63020.36442699224 iter/sec (stddev: 0.000012789196290501737) 62553.71792671868 iter/sec (stddev: 0.000012885339475167113) 0.99
benchmarks/test_guard_resolve.py::test_g1_transient_resolve 1228362.2751073993 iter/sec (stddev: 3.4767803244666425e-7) 1240854.2154731667 iter/sec (stddev: 5.049996140361579e-7) 1.01
benchmarks/test_guard_resolve.py::test_g2_cached_resolve 2303946.8203942534 iter/sec (stddev: 5.965688088563928e-8) 2385027.1922689895 iter/sec (stddev: 5.810019630007655e-8) 1.04
benchmarks/test_guard_resolve.py::test_g3_deep_chain 509762.1731168472 iter/sec (stddev: 4.961355094417616e-7) 521253.77439726394 iter/sec (stddev: 5.073789321185023e-7) 1.02
benchmarks/test_guard_resolve.py::test_g4_wide_resolve 316782.5660320029 iter/sec (stddev: 7.355729755882462e-7) 317536.6118190289 iter/sec (stddev: 6.571765628334767e-7) 1.00
benchmarks/test_guard_resolve.py::test_g5_cross_scope 1081919.1096324285 iter/sec (stddev: 3.491092252815962e-7) 1092031.6376432925 iter/sec (stddev: 4.1701638424507397e-7) 1.01

This comment was automatically generated by workflow using github-action-benchmark.

Drop the cross-file fast-path justification from the internal helper's docstring
(CLAUDE.md: cross-file rationale lives in architecture/, not raise-site docs).
Keep the flag's local meaning; the why lives in the change bundle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lesnik512
lesnik512 merged commit 7d68aec into main Jul 19, 2026
9 checks passed
@lesnik512
lesnik512 deleted the perf/cold-compile-drop-inspect-signature branch July 19, 2026 14:26
lesnik512 added a commit that referenced this pull request Jul 19, 2026
Bundles the unreleased performance work since 2.30.0: #349 (2.4x cold
first-resolve), #348 (~40% faster default child-build), #347 (uniform
dispatch-floor win). No API or behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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