From ffff33c7446e7a05981897cbeabb3d8071cad364 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Tue, 5 Aug 2025 17:20:35 +0300 Subject: [PATCH 1/4] 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; } } From 52a55219ab9f78af3b1a747c63e51da841ee3e36 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Tue, 5 Aug 2025 19:41:24 +0300 Subject: [PATCH 2/4] docs: update EN/RU quick start and tutorial with Fast Map-based lookup section; clarify performance benefit in README --- cherrypick/README.md | 7 +++++++ doc/full_tutorial_en.md | 8 ++++++++ doc/full_tutorial_ru.md | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/cherrypick/README.md b/cherrypick/README.md index 162b2b8..a63016a 100644 --- a/cherrypick/README.md +++ b/cherrypick/README.md @@ -94,6 +94,13 @@ final subScope = rootScope.openSubScope('featureScope') final dataBloc = await subScope.resolveAsync(); ``` +### Fast Dependency Lookup (Performance Improvement) + +> **Performance Note:** +> As of the latest version, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()` and related methods are now O(1) operations, regardless of the number of modules or bindings in your scope. Previously, the library had to iterate over all modules and bindings to locate the requested dependency, which could impact performance as your project grew. +> +> This optimization is internal and does not change any library APIs or usage patterns, but it significantly improves resolution speed in larger applications. + ### Dependency Lookup API - `resolve()` — Locates a dependency instance or throws if missing. diff --git a/doc/full_tutorial_en.md b/doc/full_tutorial_en.md index 0ef82de..0ee68d0 100644 --- a/doc/full_tutorial_en.md +++ b/doc/full_tutorial_en.md @@ -177,6 +177,14 @@ final service = scope.tryResolve(); // returns null if not exis --- +### Fast Dependency Lookup (Performance Improvement) + +> **Performance Note:** +> As of the latest version, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()`, `tryResolve()` and similar methods are now O(1) operations, regardless of the number of modules or bindings within your scope. Previously it would iterate over all modules and bindings, which could reduce performance as your project grew. This optimization is internal and does not affect the public API or usage patterns, but significantly improves resolution speed for larger applications. + +--- + + ## Dependency injection with annotations & code generation CherryPick supports DI with annotations, letting you eliminate manual DI setup. diff --git a/doc/full_tutorial_ru.md b/doc/full_tutorial_ru.md index 942dbaf..c298604 100644 --- a/doc/full_tutorial_ru.md +++ b/doc/full_tutorial_ru.md @@ -178,6 +178,13 @@ final service = scope.tryResolve(); // вернет null, ес --- +### Быстрый поиск зависимостей (Performance Improvement) + +> **Примечание по производительности:** +> В последних версиях CherryPick для поиска зависимости внутри scope используется Map-индекс. Благодаря этому методы `resolve()`, `tryResolve()` и аналогичные теперь работают за O(1), независимо от количества модулей и биндингов в вашем проекте. Ранее для поиска приходилось перебирать весь список вручную, что могло замедлять работу крупных приложений. Это внутреннее улучшение не меняет внешнего API или паттернов использования, но заметно ускоряет разрешение зависимостей на больших проектах. + +--- + ## Внедрение зависимостей через аннотации и автогенерацию CherryPick поддерживает DI через аннотации, что позволяет полностью избавиться от ручного внедрения зависимостей. From 05cfca59779779c1873fbb886ca7a963162bb374 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Wed, 6 Aug 2025 08:29:00 +0300 Subject: [PATCH 3/4] docs(perf): clarify Map-based resolver optimization applies since v3.0.0 in all docs --- cherrypick/README.md | 2 +- doc/full_tutorial_en.md | 2 +- doc/full_tutorial_ru.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cherrypick/README.md b/cherrypick/README.md index a63016a..f4b599d 100644 --- a/cherrypick/README.md +++ b/cherrypick/README.md @@ -97,7 +97,7 @@ final dataBloc = await subScope.resolveAsync(); ### Fast Dependency Lookup (Performance Improvement) > **Performance Note:** -> As of the latest version, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()` and related methods are now O(1) operations, regardless of the number of modules or bindings in your scope. Previously, the library had to iterate over all modules and bindings to locate the requested dependency, which could impact performance as your project grew. +> **Starting from version 3.0.0**, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()` and related methods are now O(1) operations, regardless of the number of modules or bindings in your scope. Previously, the library had to iterate over all modules and bindings to locate the requested dependency, which could impact performance as your project grew. > > This optimization is internal and does not change any library APIs or usage patterns, but it significantly improves resolution speed in larger applications. diff --git a/doc/full_tutorial_en.md b/doc/full_tutorial_en.md index 0ee68d0..03c24b9 100644 --- a/doc/full_tutorial_en.md +++ b/doc/full_tutorial_en.md @@ -180,7 +180,7 @@ final service = scope.tryResolve(); // returns null if not exis ### Fast Dependency Lookup (Performance Improvement) > **Performance Note:** -> As of the latest version, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()`, `tryResolve()` and similar methods are now O(1) operations, regardless of the number of modules or bindings within your scope. Previously it would iterate over all modules and bindings, which could reduce performance as your project grew. This optimization is internal and does not affect the public API or usage patterns, but significantly improves resolution speed for larger applications. +> **Starting from version 3.0.0**, CherryPick uses a Map-based resolver index for dependency lookup. This means calls to `resolve()`, `tryResolve()` and similar methods are now O(1) operations, regardless of the number of modules or bindings within your scope. Previously it would iterate over all modules and bindings, which could reduce performance as your project grew. This optimization is internal and does not affect the public API or usage patterns, but significantly improves resolution speed for larger applications. --- diff --git a/doc/full_tutorial_ru.md b/doc/full_tutorial_ru.md index c298604..9af1f20 100644 --- a/doc/full_tutorial_ru.md +++ b/doc/full_tutorial_ru.md @@ -181,7 +181,7 @@ final service = scope.tryResolve(); // вернет null, ес ### Быстрый поиск зависимостей (Performance Improvement) > **Примечание по производительности:** -> В последних версиях CherryPick для поиска зависимости внутри scope используется Map-индекс. Благодаря этому методы `resolve()`, `tryResolve()` и аналогичные теперь работают за O(1), независимо от количества модулей и биндингов в вашем проекте. Ранее для поиска приходилось перебирать весь список вручную, что могло замедлять работу крупных приложений. Это внутреннее улучшение не меняет внешнего API или паттернов использования, но заметно ускоряет разрешение зависимостей на больших проектах. +> Начиная с версии **3.0.0**, CherryPick для поиска зависимости внутри scope использует Map-индекс. Благодаря этому методы `resolve()`, `tryResolve()` и аналогичные теперь работают за O(1), независимо от количества модулей и биндингов в вашем проекте. Ранее для поиска приходилось перебирать весь список вручную, что могло замедлять работу крупных приложений. Это внутреннее улучшение не меняет внешнего API или паттернов использования, но заметно ускоряет разрешение зависимостей на больших проектах. --- From 70731c7e94f5ded7c5119d8f5ae6c036aaf8bac8 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Thu, 7 Aug 2025 15:48:04 +0300 Subject: [PATCH 4/4] refactor(scope): simplify _findBindingResolver with one-liner and optional chaining The function is now shorter, more readable and uses modern Dart null-safety idioms. No functional change. --- cherrypick/lib/src/scope.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cherrypick/lib/src/scope.dart b/cherrypick/lib/src/scope.dart index 7a8b74e..55cd9ec 100644 --- a/cherrypick/lib/src/scope.dart +++ b/cherrypick/lib/src/scope.dart @@ -258,11 +258,8 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin { _parentScope?.tryResolveAsync(named: named, params: params); } - BindingResolver? _findBindingResolver(String? named) { - final byType = _bindingResolvers[T]; - if (byType == null) return null; - return byType[named] as BindingResolver?; - } + BindingResolver? _findBindingResolver(String? named) => + _bindingResolvers[T]?[named] as BindingResolver?; // Индексируем все binding’и после каждого installModules/dropModules void _rebuildResolversIndex() {