From ffff33c7446e7a05981897cbeabb3d8071cad364 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Tue, 5 Aug 2025 17:20:35 +0300 Subject: [PATCH] perf(scope): speed up dependency lookup with Map-based binding resolver index Optimize resolve()/tryResolve() to use O(1) Map-based lookup by type and name instead of iterating through all modules and bindings. Behavior of factory, singleton, instance, and named bindings is preserved. --- cherrypick/lib/src/scope.dart | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cherrypick/lib/src/scope.dart b/cherrypick/lib/src/scope.dart index eb157bf..7a8b74e 100644 --- a/cherrypick/lib/src/scope.dart +++ b/cherrypick/lib/src/scope.dart @@ -39,6 +39,10 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { final Set _modulesList = HashSet(); + // индекс для мгновенного поиска binding’ов + final Map> _bindingResolvers = {}; + + /// RU: Генерирует уникальный идентификатор для скоупа. /// ENG: Generates unique identifier for scope. String _generateScopeId() { @@ -96,6 +100,7 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { for (var module in modules) { module.builder(this); } + _rebuildResolversIndex(); return this; } @@ -107,7 +112,7 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { Scope dropModules() { // [AlexeyYuPopkov](https://github.com/AlexeyYuPopkov) Thank you for the [Removed exception "ConcurrentModificationError"](https://github.com/pese-git/cherrypick/pull/2) _modulesList.clear(); - + _rebuildResolversIndex(); return this; } @@ -254,16 +259,20 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { } BindingResolver? _findBindingResolver(String? named) { + final byType = _bindingResolvers[T]; + if (byType == null) return null; + return byType[named] as BindingResolver?; + } + + // Индексируем все binding’и после каждого installModules/dropModules + void _rebuildResolversIndex() { + _bindingResolvers.clear(); for (var module in _modulesList) { for (var binding in module.bindingSet) { - if (binding.key == T && - ((!binding.isNamed && named == null) || - (binding.isNamed && named == binding.name))) { - return binding.resolver as BindingResolver?; - } + _bindingResolvers.putIfAbsent(binding.key, () => {}); + final nameKey = binding.isNamed ? binding.name : null; + _bindingResolvers[binding.key]![nameKey] = binding.resolver!; } } - - return null; } }