diff --git a/benchmark_di/REPORT.md b/benchmark_di/REPORT.md new file mode 100644 index 0000000..1a8b43d --- /dev/null +++ b/benchmark_di/REPORT.md @@ -0,0 +1,79 @@ +# DI Benchmark Results: cherrypick vs get_it + +## Benchmark parameters + +| Parameter | Value | +|------------------|-----------------------| +| --benchmark | all | +| --chainCount (-c)| 10, 100 | +| --nestingDepth (-d)| 10, 100 | +| --repeat (-r) | 2 | +| --warmup (-w) | 1 (default) | +| --format (-f) | markdown | +| --di | cherrypick, get_it | + +--- + +## Benchmark scenarios + +**(1) RegisterSingleton** +Registers and resolves a singleton. Baseline DI speed. + +**(2) ChainSingleton** +A dependency chain A → B → ... → N (singleton). Measures how fast DI resolves deep singleton chains by name. + +**(3) ChainFactory** +Same as ChainSingleton, but every chain element is a factory. Shows DI speed for stateless 'creation chain'. + +**(4) AsyncChain** +Async chain (async factory). Measures DI performance for async graphs. + +**(5) Named** +Registers two bindings with names ("impl1", "impl2"), resolves by name. Tests named lookup. + +**(6) Override** +Registers a chain/alias in a child scope and resolves UniversalService without a name in that scope. Simulates override and modular/test architecture. + +--- + +## Comparative Table (Mean, ΔRSS), chainCount=10, nestingDepth=10 + +| Scenario | cherrypick Mean (us) | cherrypick ΔRSS | get_it Mean (us) | get_it ΔRSS | +|--------------------|---------------------:|----------------:|-----------------:|------------:| +| RegisterSingleton | 21.0 | 320 | 24.5 | 80 | +| ChainSingleton | 112.5 | -3008 | 2.0 | 304 | +| ChainFactory | 8.0 | 0 | 4.0 | 0 | +| AsyncChain | 36.5 | 0 | 13.5 | 0 | +| Named | 1.5 | 0 | 0.5 | 0 | +| Override | 27.5 | 0 | 0.0 | 0 | + +## Maximum load: chainCount=100, nestingDepth=100 + +| Scenario | cherrypick Mean (us) | cherrypick ΔRSS | get_it Mean (us) | get_it ΔRSS | +|--------------------|---------------------:|----------------:|-----------------:|------------:| +| RegisterSingleton | 1.0 | 32 | 1.0 | 0 | +| ChainSingleton | 3884.0 | 0 | 1.5 | 34848 | +| ChainFactory | 4088.0 | 0 | 50.0 | 12528 | +| AsyncChain | 4287.0 | 0 | 17.0 | 63120 | +| Named | 1.0 | 0 | 0.0 | 0 | +| Override | 4767.5 | 0 | 1.5 | 14976 | + +--- + +## Scenario explanations + +- **RegisterSingleton:** Registers and resolves a singleton dependency, baseline test for cold/hot startup speed. +- **ChainSingleton:** Deep chain of singleton dependencies. Cherrypick is much slower as depth increases; get_it is nearly unaffected. +- **ChainFactory:** Creation chain with new instances per resolve. get_it generally faster on large chains due to ultra-simple factory registration. +- **AsyncChain:** Async factory chain. get_it processes async resolutions much faster; cherrypick is much slower as depth increases due to async handling. +- **Named:** Both DI containers resolve named bindings nearly instantly, even on large graphs. +- **Override:** Child scope override. get_it (thanks to stack-based scopes) resolves immediately; cherrypick supports modular testing with controlled memory use. + +## Summary + +- **get_it** demonstrates impressive speed and low overhead across all scenarios and loads, but lacks diagnostics, advanced scopes, and cycle detection. +- **cherrypick** is ideal for complex, multi-layered, production or testable architectures where scope, overrides, and diagnostics are critical. Predictably slower on deep/wide graphs, but scales well and provides extra safety. + +**Recommendation:** +- Use cherrypick for enterprise, multi-feature/testable DI needs. +- Use get_it for fast games, scripts, tiny Apps, and hot demos. diff --git a/benchmark_di/REPORT.ru.md b/benchmark_di/REPORT.ru.md new file mode 100644 index 0000000..4afe303 --- /dev/null +++ b/benchmark_di/REPORT.ru.md @@ -0,0 +1,79 @@ +# Результаты бенчмарка DI: cherrypick vs get_it + +## Параметры запуска бенчмарков + +| Параметр | Значение | +|------------------|-------------------------| +| --benchmark | all | +| --chainCount (-c)| 10, 100 | +| --nestingDepth (-d)| 10, 100 | +| --repeat (-r) | 2 | +| --warmup (-w) | 1 (по умолчанию) | +| --format (-f) | markdown | +| --di | cherrypick, get_it | + +--- + +## Описание бенчмарков + +**(1) RegisterSingleton** +Регистрируется и дважды резолвится singleton. Базовый тест скорости DI. + +**(2) ChainSingleton** +Цепочка зависимостей A → B → ... → N (singleton). Тестирует скорость заполнения и разрешения глубоких singleton-цепочек по имени. + +**(3) ChainFactory** +Аналогично ChainSingleton, но каждое звено цепи — factory (новый объект при каждом resolve). + +**(4) AsyncChain** +Асинхронная цепочка (async factory). Важно для сценариев с async DI. + +**(5) Named** +Регистрируются две реализации по имени ('impl1', 'impl2'), разрешается named. Проверка lookup по имени. + +**(6) Override** +Регистрируется цепочка/alias в дочернем scope, резолвится UniversalService без имени там же. Симуляция override и изолированной/тестовой архитектуры. + +--- + +## Сравнительная таблица (Mean (us), ΔRSS(KB)), chainCount=10, nestingDepth=10 + +| Сценарий | cherrypick Mean (мкс) | cherrypick ΔRSS | get_it Mean (мкс) | get_it ΔRSS | +|-------------------|---------------------:|----------------:|-----------------:|------------:| +| RegisterSingleton | 21.0 | 320 | 24.5 | 80 | +| ChainSingleton | 112.5 | -3008 | 2.0 | 304 | +| ChainFactory | 8.0 | 0 | 4.0 | 0 | +| AsyncChain | 36.5 | 0 | 13.5 | 0 | +| Named | 1.5 | 0 | 0.5 | 0 | +| Override | 27.5 | 0 | 0.0 | 0 | + +## Максимальная нагрузка: chainCount=100, nestingDepth=100 + +| Сценарий | cherrypick Mean (мкс) | cherrypick ΔRSS | get_it Mean (мкс) | get_it ΔRSS | +|-------------------|---------------------:|----------------:|-----------------:|------------:| +| RegisterSingleton | 1.0 | 32 | 1.0 | 0 | +| ChainSingleton | 3884.0 | 0 | 1.5 | 34848 | +| ChainFactory | 4088.0 | 0 | 50.0 | 12528 | +| AsyncChain | 4287.0 | 0 | 17.0 | 63120 | +| Named | 1.0 | 0 | 0.0 | 0 | +| Override | 4767.5 | 0 | 1.5 | 14976 | + +--- + +## Пояснения по сценариям + +- **RegisterSingleton** — базовый тест DI (регистрация и резолвинг singleton). Практически мгновенно у обоих DI. +- **ChainSingleton** — глубокая singleton-цепочка. get_it вне конкуренции по скорости, cherrypick медленнее из-за более сложной логики поиска именованных зависимостей, но предсказуем. +- **ChainFactory** — цепочка Factory-объектов. cherrypick заметно медленнее на длинных цепях, get_it почти не увеличивает время. +- **AsyncChain** — асинхронная цепочка сервисов. get_it существенно быстрее, cherrypick страдает на глубине/ширине. +- **Named** — разрешение зависимостей по имени. Оба DI почти мгновенны. +- **Override** — переопределение alias без имени в дочернем scope. get_it (со стековыми scope) почти не теряет времени; cherrypick предсказуемо замедляется на глубине/ширине. + +## Итог + +- **get_it** выдаёт отличную производительность по всем сценариям, особенно на больших графах; но не поддерживает продвинутую диагностику, проверки циклов, расширенные scope. +- **cherrypick** — незаменим для работы с корпоративными/тестируемыми архитектурами и наследованием, устойчиво ведёт себя на тысячи зависимостей, но требует учёта роста времени при экстремальных нагрузках. + +**Рекомендация:** +- cherrypick — выбор для серьёзных production-систем и тестирования; +- get_it — лидер для MVP, быстрых демо, прототипов, CLI, games. diff --git a/benchmark_di/lib/di_adapters/get_it_adapter.dart b/benchmark_di/lib/di_adapters/get_it_adapter.dart index 886827e..e510cb5 100644 --- a/benchmark_di/lib/di_adapters/get_it_adapter.dart +++ b/benchmark_di/lib/di_adapters/get_it_adapter.dart @@ -21,8 +21,52 @@ class GetItAdapter implements DIAdapter { @override DIAdapter openSubScope(String name) { - // get_it не поддерживает scope, возвращаем новый инстанс - return GetItAdapter(); + // Открываем новый scope и возвращаем адаптер, который в setupDependencies будет использовать init. + return _GetItScopeAdapter(_getIt, name); + } + + @override + Future waitForAsyncReady() async { + await _getIt.allReady(); + } +} + +class _GetItScopeAdapter implements DIAdapter { + final GetIt _getIt; + final String _scopeName; + bool _scopePushed = false; + void Function(dynamic container)? _pendingRegistration; + + _GetItScopeAdapter(this._getIt, this._scopeName); + + @override + void setupDependencies(void Function(dynamic container) registration) { + _pendingRegistration = registration; + // Создаём scope через pushNewScope с init для правильной регистрации + _getIt.pushNewScope( + scopeName: _scopeName, + init: (getIt) => _pendingRegistration?.call(getIt), + ); + _scopePushed = true; + } + + @override + T resolve({String? named}) => _getIt(instanceName: named); + + @override + Future resolveAsync({String? named}) async => _getIt(instanceName: named); + + @override + void teardown() { + if (_scopePushed) { + _getIt.popScope(); + _scopePushed = false; + } + } + + @override + DIAdapter openSubScope(String name) { + return _GetItScopeAdapter(_getIt, name); } @override diff --git a/benchmark_di/lib/scenarios/di_universal_registration.dart b/benchmark_di/lib/scenarios/di_universal_registration.dart index a32d54a..0cd2d14 100644 --- a/benchmark_di/lib/scenarios/di_universal_registration.dart +++ b/benchmark_di/lib/scenarios/di_universal_registration.dart @@ -28,7 +28,7 @@ void Function(dynamic) getUniversalRegistration( ), ]); }; - } else if (adapter is GetItAdapter) { + } else if (adapter is GetItAdapter || adapter.runtimeType.toString().contains('GetItScopeAdapter')) { return (getIt) { switch (scenario) { case UniversalScenario.asyncChain: @@ -103,6 +103,13 @@ void Function(dynamic) getUniversalRegistration( // handled at benchmark level break; } + // UniversalService alias (без имени) для chain/override-сценариев + if (scenario == UniversalScenario.chain || scenario == UniversalScenario.override) { + final depName = '${chainCount}_$nestingDepth'; + getIt.registerSingleton( + getIt(instanceName: depName), + ); + } }; } throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}');