Skip to content

refactor: dispatch the dependency graph by provider type, make the provider step a free function (#617) - #629

Merged
lesnik512 merged 1 commit into
mainfrom
perf-617-c
Oct 9, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
perf-617-c

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Part of #617 (items 7 and 8). Closes #617. No public API change.

Summary

  • dependency_graph dispatches on the closed provider set by type, the same way resolver_compiler does. The virtual hooks _get_dependencies, _redirect_target and _iter_validation_issues are gone from AbstractProvider, Factory and Alias; dependency_graph now has redirect_target and dependencies_of, and collect_errors reports unwireable Factory parameters itself.
  • The _ignores_scope ClassVar added in fix: skip scope checks on Container injection in validate() (#612) #620 is replaced by dependency_chain[-1] is container_provider, so there is one dispatch mechanism instead of two.
  • AbstractProvider._resolution_step is now the free function provider_step(provider, scope=None) in exceptions/rendering.py. Callers that need a zero-argument step bind it with functools.partial.
  • Factory._argument_resolution_error is now wiring.argument_resolution_error, next to WiringPlan.unwireable. Factory no longer imports suggester.
  • The stale cycle note at the top of dependency_graph.py is rewritten: only importing Container would cycle.
  • AGENTS.md says a new provider type also needs branches in dependency_graph, which fall through silently.

Design decisions

Performance

Interleaved A/B against #626 + #628, 14 rounds, paired median (A/A noise is about ±2.5%): G10 build + validate -4.9%; G8 cold +1.5%, inside noise; every other scenario within ±1%.

Test plan

  • just lint-ci
  • full suite, 100% line and branch coverage
  • just test-race

@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: 368f21f Previous: 4ff73aa Ratio
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type 4495303.509188955 iter/sec (stddev: 4.130700358559182e-9) 2883405.740465237 iter/sec (stddev: 8.889110093741167e-9) 0.64
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry 4393297.28691088 iter/sec (stddev: 1.3655638403767411e-8) 2855075.2116767764 iter/sec (stddev: 9.309855419546196e-9) 0.65
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve 23442.736708711625 iter/sec (stddev: 0.000044770248239260135) 24340.53600966804 iter/sec (stddev: 0.000043030297095546176) 1.04
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached 19113.26348828863 iter/sec (stddev: 0.0001384024382391895) 19087.243953149085 iter/sec (stddev: 0.00013892428205748751) 1.00
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] 547.2620530427952 iter/sec (stddev: 0.00011673943749696228) 374.0257215909595 iter/sec (stddev: 0.00004170459432905435) 0.68
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] 539.3071860161772 iter/sec (stddev: 0.00009171101100072757) 357.8080831726214 iter/sec (stddev: 0.00008839544842994853) 0.66
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] 512.7451446978226 iter/sec (stddev: 0.00006790238780643825) 348.63113879817206 iter/sec (stddev: 0.00009581006421014357) 0.68
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] 15833.42393563162 iter/sec (stddev: 0.000008047411261998512) 12587.970243286616 iter/sec (stddev: 0.00004275495653516427) 0.80
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] 7084.026830045798 iter/sec (stddev: 0.00002070785777983849) 6114.5138380448025 iter/sec (stddev: 0.000012780478478524728) 0.86
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] 3732.0970139929277 iter/sec (stddev: 0.0000384159133709794) 3367.8641378764446 iter/sec (stddev: 0.000041659057409168) 0.90
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] 11109.590743054341 iter/sec (stddev: 0.00000885535957794347) 9217.86068044916 iter/sec (stddev: 0.00000935387959642552) 0.83
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] 4533.39074621599 iter/sec (stddev: 0.000023146996249073897) 3934.8513224509657 iter/sec (stddev: 0.00004120578958358568) 0.87
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] 2086.6593088758946 iter/sec (stddev: 0.000040863800307781456) 1897.565913138107 iter/sec (stddev: 0.000042475565665712956) 0.91
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] 36853.66729283851 iter/sec (stddev: 0.000005471958123528633) 40008.121648654465 iter/sec (stddev: 0.000006876301252925549) 1.09
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] 14976.862619390235 iter/sec (stddev: 0.00001603582282394068) 13109.819648304185 iter/sec (stddev: 0.000011772923051481861) 0.88
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] 6191.6523833006 iter/sec (stddev: 0.000033662863947301256) 5893.357920429329 iter/sec (stddev: 0.000031517902397323976) 0.95
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container 1743646.023111904 iter/sec (stddev: 2.7825025711581113e-8) 1589390.3742276558 iter/sec (stddev: 3.008854030860649e-8) 0.91
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope 1905100.3775987742 iter/sec (stddev: 1.4413810602939395e-8) 1655887.5288100082 iter/sec (stddev: 2.6907570556999255e-8) 0.87
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch 3575.4211659568164 iter/sec (stddev: 0.00000777005025252269) 2998.459393278227 iter/sec (stddev: 0.000011594545473644167) 0.84
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control 68581.74713105979 iter/sec (stddev: 0.0000018825160600172051) 58320.38401458291 iter/sec (stddev: 0.000002060952743492397) 0.85
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync 464318.4396151828 iter/sec (stddev: 9.885483991657149e-8) 376103.53949892963 iter/sec (stddev: 8.654240798812594e-8) 0.81
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale 68411.66263972252 iter/sec (stddev: 0.0000011099926786051772) 57111.16783367965 iter/sec (stddev: 0.0000017513335188553384) 0.83
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers 632.2478632783732 iter/sec (stddev: 0.0010776273027091076) 521.7209230521734 iter/sec (stddev: 0.001402826024751173) 0.83
benchmarks/test_guard_resolve.py::test_g1_transient_resolve 2373550.2948261104 iter/sec (stddev: 2.3144126754821485e-8) 2074878.6351624003 iter/sec (stddev: 3.276435272397166e-8) 0.87
benchmarks/test_guard_resolve.py::test_g2_cached_resolve 4420975.35293048 iter/sec (stddev: 9.588399244779715e-9) 2967918.0230213017 iter/sec (stddev: 2.0473202345492446e-8) 0.67
benchmarks/test_guard_resolve.py::test_g3_deep_chain 785110.1812848137 iter/sec (stddev: 6.420811528484894e-8) 729288.0285512193 iter/sec (stddev: 7.439908147066853e-8) 0.93
benchmarks/test_guard_resolve.py::test_g4_wide_resolve 513999.85253215564 iter/sec (stddev: 3.984019549565078e-7) 450713.2954819865 iter/sec (stddev: 4.066462596101566e-7) 0.88
benchmarks/test_guard_resolve.py::test_g5_cross_scope 2117446.611365043 iter/sec (stddev: 1.5884132457867998e-8) 1871349.7568507271 iter/sec (stddev: 2.952013145968033e-8) 0.88
benchmarks/test_guard_resolve.py::test_g9_context_resolve 1649955.3687103158 iter/sec (stddev: 1.7982965377398072e-7) 1322038.2352682091 iter/sec (stddev: 1.5385006162761187e-7) 0.80
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve 825841.9830640745 iter/sec (stddev: 2.4990759111447557e-8) 739381.1830524713 iter/sec (stddev: 6.086383291251184e-8) 0.90
benchmarks/test_guard_resolve.py::test_g18_alias_hop 4399035.062868196 iter/sec (stddev: 1.1950819248245055e-8) 2906313.19234593 iter/sec (stddev: 1.5861470107359007e-8) 0.66
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain 39950.61198811231 iter/sec (stddev: 0.000019565790364117832) 34731.80562784902 iter/sec (stddev: 0.00002043573443514095) 0.87
benchmarks/test_guard_validate.py::test_g11_validate_wide 24164.920167221884 iter/sec (stddev: 0.000024852409141282664) 18375.912926832763 iter/sec (stddev: 0.0002530440259825077) 0.76

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

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.

Refactor and perf round two: inline cache item, close_sync loop, cheaper root build, type dispatch in dependency_graph

1 participant