Conversation
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: 3e7a791 | Previous: 634b5d5 | Ratio |
|---|---|---|---|
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type |
4981292.010641465 iter/sec (stddev: 9.487427844623191e-9) |
5067365.557824091 iter/sec (stddev: 5.37767770073532e-9) |
1.02 |
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry |
5000452.040863807 iter/sec (stddev: 9.343735342179386e-9) |
5043963.437488808 iter/sec (stddev: 7.963377171912855e-9) |
1.01 |
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve |
29805.53521449727 iter/sec (stddev: 0.000040084172147560215) |
29133.821939481844 iter/sec (stddev: 0.000042652352964517597) |
0.98 |
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached |
24411.353694888472 iter/sec (stddev: 0.00009057725546289804) |
23840.930197388618 iter/sec (stddev: 0.00011952106627701537) |
0.98 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] |
621.8270278136808 iter/sec (stddev: 0.00005337979229519717) |
644.2806973147967 iter/sec (stddev: 0.0000846566876096164) |
1.04 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] |
590.8287114448974 iter/sec (stddev: 0.000019976937052412193) |
604.7983741696503 iter/sec (stddev: 0.00004472488954112076) |
1.02 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] |
532.5946580879837 iter/sec (stddev: 0.0000332363272962729) |
546.54110039303 iter/sec (stddev: 0.00003286458174956396) |
1.03 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] |
2760.895223034518 iter/sec (stddev: 0.00012265734892447116) |
2731.1064559265274 iter/sec (stddev: 0.00012718492159027194) |
0.99 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] |
2130.6096377266267 iter/sec (stddev: 0.00012692101576091707) |
2149.5744093415256 iter/sec (stddev: 0.00012977853814105617) |
1.01 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] |
1458.6335003554445 iter/sec (stddev: 0.00013398828158956642) |
1451.301452763851 iter/sec (stddev: 0.0001383443306096785) |
0.99 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] |
2755.205438331725 iter/sec (stddev: 0.00012765086047143004) |
2738.208884638193 iter/sec (stddev: 0.00013448064777326195) |
0.99 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] |
1549.0911037121668 iter/sec (stddev: 0.001159125077254878) |
1587.9677565870695 iter/sec (stddev: 0.0010877411031153322) |
1.03 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] |
1165.1157244881051 iter/sec (stddev: 0.0001506791128619846) |
1174.8788002463164 iter/sec (stddev: 0.0001331872057788412) |
1.01 |
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
1300252.3139636046 iter/sec (stddev: 2.429005056612874e-8) |
1216487.0736319197 iter/sec (stddev: 3.868375187039108e-8) |
0.94 |
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope |
1201379.7846842627 iter/sec (stddev: 1.9593283518912474e-8) |
1167505.7238495366 iter/sec (stddev: 2.571346495064066e-8) |
0.97 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch |
3395.4306137881895 iter/sec (stddev: 0.000009881458759857972) |
3209.149000072793 iter/sec (stddev: 0.0000057248097968980084) |
0.95 |
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control |
76690.1858587268 iter/sec (stddev: 8.819212568534428e-7) |
72204.87359413861 iter/sec (stddev: 9.83895866943126e-7) |
0.94 |
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale |
59904.103836629845 iter/sec (stddev: 0.0000011106143068814483) |
56546.36149726006 iter/sec (stddev: 0.000001241191563541163) |
0.94 |
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers |
793.1009913862559 iter/sec (stddev: 0.000018028956401860107) |
737.6555880965986 iter/sec (stddev: 0.000013137261314336917) |
0.93 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
2913519.4586803494 iter/sec (stddev: 2.5197674338043362e-8) |
2985272.2341429708 iter/sec (stddev: 1.923460761314064e-8) |
1.02 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
4827161.316815463 iter/sec (stddev: 1.2249410075070706e-8) |
4923011.727339974 iter/sec (stddev: 1.413513816374172e-8) |
1.02 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
1047346.8322012024 iter/sec (stddev: 2.9435513104908763e-8) |
1038111.7556904309 iter/sec (stddev: 6.860766139525715e-8) |
0.99 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
616418.9474113911 iter/sec (stddev: 3.737441898263821e-7) |
622698.1961204013 iter/sec (stddev: 4.1214097716581753e-7) |
1.01 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
2746469.550720356 iter/sec (stddev: 2.1521869645192017e-8) |
2823383.260179447 iter/sec (stddev: 1.7493167123526233e-8) |
1.03 |
benchmarks/test_guard_resolve.py::test_g9_context_resolve |
2064749.0860197744 iter/sec (stddev: 1.3770991094387001e-7) |
1985004.1866217137 iter/sec (stddev: 1.3030298794803647e-7) |
0.96 |
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve |
1023456.1304999011 iter/sec (stddev: 4.846441082000878e-8) |
1035928.6470580541 iter/sec (stddev: 2.5029174039763364e-8) |
1.01 |
benchmarks/test_guard_resolve.py::test_g18_alias_hop |
4134785.403819091 iter/sec (stddev: 4.0110506357322505e-8) |
4939495.868006965 iter/sec (stddev: 8.04150562674476e-9) |
1.19 |
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain |
38828.88460007932 iter/sec (stddev: 0.000016732231345892298) |
38243.11779742017 iter/sec (stddev: 0.000017162106924872293) |
0.98 |
benchmarks/test_guard_validate.py::test_g11_validate_wide |
22011.215697566455 iter/sec (stddev: 0.000021912780115256454) |
21418.523692909832 iter/sec (stddev: 0.000024010257486729216) |
0.97 |
This comment was automatically generated by workflow using github-action-benchmark.
4 tasks done
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.
Closes #574.
Summary
Internal cleanup with no behaviour change. The generated resolver source is byte-identical for every shape: I dumped
_sourceand the line-to-argument map for arity 0 to 3, positional and keyword (including a non-identifier name), with and without static kwargs, cached and transient, before and after the change, and the two dumps match.Each bullet of the issue, checked against current main (634b5d5):
_TRANSIENTand_CACHEDare now concatenated from three module-level fragments:_NAVIGATE(scope lookup and closed check),_BUILD_ARGUMENTS(the argumenttrywith bothexceptclauses) and_CALL_CREATOR(the creator call and its handlers). Composition happens once at import, so the hot path is unchanged.arg_linesby suffix matching._sourcenow returns(source, arg_lines). It knows which emitted build line holds eachr{i}(target)call and offsets that by the template line of{build}._codeno longer scans the rendered lines._navigate(scope: typing.Any). Now annotatedenum.IntEnum.performance.md. It now points at ADR-0001.ResolutionStepbuilders. There is one now:AbstractProvider._resolution_step(scope=None), which defaults to the provider's own scope.Factory._resolution_stepis gone (the base method covers it), andrendering.pylostprovider_stepandredirect_steps, so it only draws. The callers areredirect_hopsandbuild_cycle_errorindependency_graph.py,InvalidScopeDependencyError._render_body, and the alias and context-provider resolvers in the compiler.render_chain,render_suggestion_linesandrender_suggestionsare renamed in place inexceptions/rendering.py.deeper_membersandnext_deepermove out of the publicmodern_di.scopeinto a new private module,modern_di/_scope_algebra.py, which, likescope.py, imports onlyenum. The import-only-enum invariant test now covers both modules. Tests and one benchmark comment are updated to match._scope_detailand_next_deeper_memostay private because only their own module uses them.CircularDependencyError.prepend_stepdocstring. It no longer mentions the interpreted and compiled paths. It says the cycle already names every node, so an outer frame has nothing to add.Nothing in the issue had already been done by #583 to #592. Those PRs only moved line numbers and renamed the provider internals the issue refers to.
Design decisions
CreatorCallError.from_type_errorand_navigatekeep theirCallable[[], ResolutionStep]contract.functools.partial(provider_step, cp, scope)at compile time. It now passescp._resolution_step, which readsscopewhen the error is built. A registered provider's scope is frozen, so the result is the same.modern_di.scopeexposes onlyScopeagain.InvalidScopeDependencyErrorstill draws its redirect hops itself instead of callingdependency_graph.redirect_hops.exceptionscannot importdependency_graphwithout a cycle, so the rule that hops are drawn at the effective scope is written in both places.Perf
Best of 7 runs of 1M
resolve_providercalls from a REQUEST container, Python fromuv run:The generated code is identical, so this difference is run-to-run noise.
Test plan
just lintandjust lint-cicleanjust test-ci: 616 passed, 100% line coverage, includingtest_resolve_costs_exactly_one_resolver_frame_per_node,test_alias_hop_costs_no_resolver_frameandtest_every_resolver_shape_compiles_and_resolvesarg_linesbyte-identical before and after for every shapejust adr-checkpassesmkdocs build --strictpasses