perf(compiler): drop compile-time inspect.signature from positional fast-path predicate#349
Merged
Merged
Conversation
…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>
There was a problem hiding this comment.
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>
This was referenced Jul 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
_positional_names(the compile-time predicate that decides whether a Factory's creator can be called positionally) re-ran a fullinspect.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_creatordrops the positional-only-with-default param fromparsed_kwargs. This records ahas_positional_only_gapbool onFactoryand 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.signatureat ~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.test_first_resolve_does_not_reintrospect_creator): red before, green after — a first resolve must not callinspect.signature.🤖 Generated with Claude Code