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.
This commit is contained in:
Sergey Penkovsky
2025-08-05 17:20:35 +03:00
parent a4573ce8ef
commit ffff33c744

View File

@@ -39,6 +39,10 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
final Set<Module> _modulesList = HashSet(); final Set<Module> _modulesList = HashSet();
// индекс для мгновенного поиска bindingов
final Map<Object, Map<String?, BindingResolver>> _bindingResolvers = {};
/// RU: Генерирует уникальный идентификатор для скоупа. /// RU: Генерирует уникальный идентификатор для скоупа.
/// ENG: Generates unique identifier for scope. /// ENG: Generates unique identifier for scope.
String _generateScopeId() { String _generateScopeId() {
@@ -96,6 +100,7 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
for (var module in modules) { for (var module in modules) {
module.builder(this); module.builder(this);
} }
_rebuildResolversIndex();
return this; return this;
} }
@@ -107,7 +112,7 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
Scope dropModules() { Scope dropModules() {
// [AlexeyYuPopkov](https://github.com/AlexeyYuPopkov) Thank you for the [Removed exception "ConcurrentModificationError"](https://github.com/pese-git/cherrypick/pull/2) // [AlexeyYuPopkov](https://github.com/AlexeyYuPopkov) Thank you for the [Removed exception "ConcurrentModificationError"](https://github.com/pese-git/cherrypick/pull/2)
_modulesList.clear(); _modulesList.clear();
_rebuildResolversIndex();
return this; return this;
} }
@@ -254,16 +259,20 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
} }
BindingResolver<T>? _findBindingResolver<T>(String? named) { BindingResolver<T>? _findBindingResolver<T>(String? named) {
for (var module in _modulesList) { final byType = _bindingResolvers[T];
for (var binding in module.bindingSet) { if (byType == null) return null;
if (binding.key == T && return byType[named] as BindingResolver<T>?;
((!binding.isNamed && named == null) ||
(binding.isNamed && named == binding.name))) {
return binding.resolver as BindingResolver<T>?;
}
}
} }
return null; // Индексируем все bindingи после каждого installModules/dropModules
void _rebuildResolversIndex() {
_bindingResolvers.clear();
for (var module in _modulesList) {
for (var binding in module.bindingSet) {
_bindingResolvers.putIfAbsent(binding.key, () => {});
final nameKey = binding.isNamed ? binding.name : null;
_bindingResolvers[binding.key]![nameKey] = binding.resolver!;
}
}
} }
} }