diff --git a/benchmark_cherrypick/lib/benchmarks/universal_chain_async_benchmark.dart b/benchmark_cherrypick/lib/benchmarks/universal_chain_async_benchmark.dart index 569878a..a45102d 100644 --- a/benchmark_cherrypick/lib/benchmarks/universal_chain_async_benchmark.dart +++ b/benchmark_cherrypick/lib/benchmarks/universal_chain_async_benchmark.dart @@ -2,6 +2,7 @@ import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:benchmark_cherrypick/di_adapters/di_adapter.dart'; import 'package:benchmark_cherrypick/scenarios/universal_chain_module.dart'; import 'package:benchmark_cherrypick/scenarios/universal_service.dart'; +import 'package:benchmark_cherrypick/scenarios/di_universal_registration.dart'; class UniversalChainAsyncBenchmark extends AsyncBenchmarkBase { final DIAdapter di; @@ -18,16 +19,14 @@ class UniversalChainAsyncBenchmark extends AsyncBenchmarkBase { @override Future setup() async { - di.setupDependencies((scope) { - scope.installModules([ - UniversalChainModule( - chainCount: chainCount, - nestingDepth: nestingDepth, - bindingMode: mode, - scenario: UniversalScenario.asyncChain, - ), - ]); - }); + di.setupDependencies(getUniversalRegistration( + di, + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: mode, + scenario: UniversalScenario.asyncChain, + )); + await di.waitForAsyncReady(); } @override diff --git a/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart b/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart index 2d33b90..1068708 100644 --- a/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart +++ b/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart @@ -2,6 +2,7 @@ import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:benchmark_cherrypick/di_adapters/di_adapter.dart'; import 'package:benchmark_cherrypick/scenarios/universal_chain_module.dart'; import 'package:benchmark_cherrypick/scenarios/universal_service.dart'; +import 'package:benchmark_cherrypick/scenarios/di_universal_registration.dart'; class UniversalChainBenchmark extends BenchmarkBase { final DIAdapter _di; @@ -23,39 +24,30 @@ class UniversalChainBenchmark extends BenchmarkBase { void setup() { switch (scenario) { case UniversalScenario.override: - _di.setupDependencies((scope) { - scope.installModules([ - UniversalChainModule( - chainCount: chainCount, - nestingDepth: nestingDepth, - bindingMode: UniversalBindingMode.singletonStrategy, - scenario: UniversalScenario.register, - ), - ]); - }); + _di.setupDependencies(getUniversalRegistration( + _di, + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: UniversalBindingMode.singletonStrategy, + scenario: UniversalScenario.chain, + )); _childDi = _di.openSubScope('child'); - _childDi!.setupDependencies((scope) { - scope.installModules([ - UniversalChainModule( - chainCount: chainCount, - nestingDepth: nestingDepth, - bindingMode: UniversalBindingMode.singletonStrategy, - scenario: UniversalScenario.register, - ), - ]); - }); + _childDi!.setupDependencies(getUniversalRegistration( + _childDi!, + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: UniversalBindingMode.singletonStrategy, + scenario: UniversalScenario.chain, // критично: цепочку, а не просто alias! + )); break; default: - _di.setupDependencies((scope) { - scope.installModules([ - UniversalChainModule( - chainCount: chainCount, - nestingDepth: nestingDepth, - bindingMode: mode, - scenario: scenario, - ), - ]); - }); + _di.setupDependencies(getUniversalRegistration( + _di, + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: mode, + scenario: scenario, + )); break; } } @@ -70,7 +62,11 @@ class UniversalChainBenchmark extends BenchmarkBase { _di.resolve(); break; case UniversalScenario.named: - _di.resolve(named: 'impl2'); + if (_di.runtimeType.toString().contains('GetItAdapter')) { + _di.resolve(named: 'impl2'); + } else { + _di.resolve(named: 'impl2'); + } break; case UniversalScenario.chain: final serviceName = '${chainCount}_$nestingDepth'; diff --git a/benchmark_cherrypick/lib/cli/benchmark_cli.dart b/benchmark_cherrypick/lib/cli/benchmark_cli.dart index 61ca734..0db499a 100644 --- a/benchmark_cherrypick/lib/cli/benchmark_cli.dart +++ b/benchmark_cherrypick/lib/cli/benchmark_cli.dart @@ -11,6 +11,7 @@ import 'runner.dart'; import 'package:benchmark_cherrypick/benchmarks/universal_chain_benchmark.dart'; import 'package:benchmark_cherrypick/benchmarks/universal_chain_async_benchmark.dart'; import 'package:benchmark_cherrypick/di_adapters/cherrypick_adapter.dart'; +import 'package:benchmark_cherrypick/di_adapters/get_it_adapter.dart'; /// Command-line interface (CLI) runner for benchmarks. /// @@ -28,8 +29,8 @@ class BenchmarkCliRunner { for (final c in config.chainCounts) { for (final d in config.nestDepths) { BenchmarkResult benchResult; + final di = config.di == 'getit' ? GetItAdapter() : CherrypickDIAdapter(); if (scenario == UniversalScenario.asyncChain) { - final di = CherrypickDIAdapter(); final benchAsync = UniversalChainAsyncBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, ); @@ -39,7 +40,6 @@ class BenchmarkCliRunner { repeats: config.repeats, ); } else { - final di = CherrypickDIAdapter(); final benchSync = UniversalChainBenchmark(di, chainCount: c, nestingDepth: d, mode: mode, scenario: scenario, ); diff --git a/benchmark_cherrypick/lib/cli/parser.dart b/benchmark_cherrypick/lib/cli/parser.dart index 11ad208..73ee533 100644 --- a/benchmark_cherrypick/lib/cli/parser.dart +++ b/benchmark_cherrypick/lib/cli/parser.dart @@ -81,6 +81,8 @@ class BenchmarkCliConfig { final int warmups; /// Output report format. final String format; + /// Name of DI implementation ("cherrypick" or "getit") + final String di; BenchmarkCliConfig({ required this.benchesToRun, required this.chainCounts, @@ -88,6 +90,7 @@ class BenchmarkCliConfig { required this.repeats, required this.warmups, required this.format, + required this.di, }); } @@ -101,6 +104,7 @@ BenchmarkCliConfig parseBenchmarkCli(List args) { ..addOption('repeat', abbr: 'r', defaultsTo: '2') ..addOption('warmup', abbr: 'w', defaultsTo: '1') ..addOption('format', abbr: 'f', defaultsTo: 'pretty') + ..addOption('di', defaultsTo: 'cherrypick', help: 'DI implementation: cherrypick or getit') ..addFlag('help', abbr: 'h', negatable: false, help: 'Show help'); final result = parser.parse(args); if (result['help'] == true) { @@ -120,5 +124,6 @@ BenchmarkCliConfig parseBenchmarkCli(List args) { repeats: int.tryParse(result['repeat'] as String? ?? "") ?? 2, warmups: int.tryParse(result['warmup'] as String? ?? "") ?? 1, format: result['format'] as String, + di: result['di'] as String? ?? 'cherrypick', ); } \ No newline at end of file diff --git a/benchmark_cherrypick/lib/di_adapters/cherrypick_adapter.dart b/benchmark_cherrypick/lib/di_adapters/cherrypick_adapter.dart index 2b002bd..fd44e2f 100644 --- a/benchmark_cherrypick/lib/di_adapters/cherrypick_adapter.dart +++ b/benchmark_cherrypick/lib/di_adapters/cherrypick_adapter.dart @@ -12,11 +12,11 @@ class CherrypickDIAdapter implements DIAdapter { } @override - T resolve({String? named}) => + T resolve({String? named}) => named == null ? _scope!.resolve() : _scope!.resolve(named: named); @override - Future resolveAsync({String? named}) async => + Future resolveAsync({String? named}) async => named == null ? await _scope!.resolveAsync() : await _scope!.resolveAsync(named: named); @override @@ -30,6 +30,9 @@ class CherrypickDIAdapter implements DIAdapter { final sub = _scope!.openSubScope(name); return _CherrypickSubScopeAdapter(sub); } + + @override + Future waitForAsyncReady() async {} } /// Internal adapter for a CherryPick sub-scope (callbacks based). @@ -43,11 +46,11 @@ class _CherrypickSubScopeAdapter extends CherrypickDIAdapter { } @override - T resolve({String? named}) => + T resolve({String? named}) => named == null ? _subScope.resolve() : _subScope.resolve(named: named); @override - Future resolveAsync({String? named}) async => + Future resolveAsync({String? named}) async => named == null ? await _subScope.resolveAsync() : await _subScope.resolveAsync(named: named); @override diff --git a/benchmark_cherrypick/lib/di_adapters/di_adapter.dart b/benchmark_cherrypick/lib/di_adapters/di_adapter.dart index 269f19b..34aebd7 100644 --- a/benchmark_cherrypick/lib/di_adapters/di_adapter.dart +++ b/benchmark_cherrypick/lib/di_adapters/di_adapter.dart @@ -8,14 +8,17 @@ abstract class DIAdapter { void setupDependencies(void Function(dynamic container) registration); /// Резолвит (возвращает) экземпляр типа [T] (по имени, если требуется). - T resolve({String? named}); + T resolve({String? named}); /// Асинхронно резолвит экземпляр типа [T]. - Future resolveAsync({String? named}); + Future resolveAsync({String? named}); /// Уничтожает/отчищает DI-контейнер. void teardown(); /// Открывает дочерний под-scope (если применимо). DIAdapter openSubScope(String name); + + /// Ожидание готовности DI контейнера (нужно для async DI, например get_it) + Future waitForAsyncReady(); } diff --git a/benchmark_cherrypick/lib/di_adapters/get_it_adapter.dart b/benchmark_cherrypick/lib/di_adapters/get_it_adapter.dart new file mode 100644 index 0000000..886827e --- /dev/null +++ b/benchmark_cherrypick/lib/di_adapters/get_it_adapter.dart @@ -0,0 +1,32 @@ +import 'package:get_it/get_it.dart'; +import 'di_adapter.dart'; + +class GetItAdapter implements DIAdapter { + late GetIt _getIt; + + @override + void setupDependencies(void Function(dynamic container) registration) { + _getIt = GetIt.asNewInstance(); + registration(_getIt); + } + + @override + T resolve({String? named}) => _getIt(instanceName: named); + + @override + Future resolveAsync({String? named}) async => _getIt(instanceName: named); + + @override + void teardown() => _getIt.reset(); + + @override + DIAdapter openSubScope(String name) { + // get_it не поддерживает scope, возвращаем новый инстанс + return GetItAdapter(); + } + + @override + Future waitForAsyncReady() async { + await _getIt.allReady(); + } +} diff --git a/benchmark_cherrypick/lib/scenarios/di_universal_registration.dart b/benchmark_cherrypick/lib/scenarios/di_universal_registration.dart new file mode 100644 index 0000000..d0351af --- /dev/null +++ b/benchmark_cherrypick/lib/scenarios/di_universal_registration.dart @@ -0,0 +1,109 @@ +import 'package:benchmark_cherrypick/scenarios/universal_service.dart'; + +import '../di_adapters/di_adapter.dart'; +import '../di_adapters/cherrypick_adapter.dart'; +import '../di_adapters/get_it_adapter.dart'; +import 'universal_chain_module.dart'; +import 'universal_service.dart'; +import 'package:get_it/get_it.dart'; +import 'package:cherrypick/cherrypick.dart'; + +/// Возвращает универсальную функцию регистрации зависимостей, +/// подходящую под выбранный DI-адаптер. +void Function(dynamic) getUniversalRegistration( + DIAdapter adapter, { + required int chainCount, + required int nestingDepth, + required UniversalBindingMode bindingMode, + required UniversalScenario scenario, +}) { + if (adapter is CherrypickDIAdapter) { + return (scope) { + scope.installModules([ + UniversalChainModule( + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: bindingMode, + scenario: scenario, + ), + ]); + }; + } else if (adapter is GetItAdapter) { + return (getIt) { + switch (scenario) { + case UniversalScenario.asyncChain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + getIt.registerSingletonAsync( + () async { + final prev = level > 1 + ? await getIt.getAsync(instanceName: prevDepName) + : null; + return UniversalServiceImpl(value: depName, dependency: prev); + }, + instanceName: depName, + ); + } + } + break; + case UniversalScenario.register: + getIt.registerSingleton(UniversalServiceImpl(value: 'reg', dependency: null)); + break; + case UniversalScenario.named: + getIt.registerFactory(() => UniversalServiceImpl(value: 'impl1'), instanceName: 'impl1'); + getIt.registerFactory(() => UniversalServiceImpl(value: 'impl2'), instanceName: 'impl2'); + break; + case UniversalScenario.chain: + for (int chain = 1; chain <= chainCount; chain++) { + for (int level = 1; level <= nestingDepth; level++) { + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + switch (bindingMode) { + case UniversalBindingMode.singletonStrategy: + getIt.registerSingleton( + UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? getIt(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + case UniversalBindingMode.factoryStrategy: + getIt.registerFactory( + () => UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? getIt(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + case UniversalBindingMode.asyncStrategy: + // getIt не поддерживает асинх. factory напрямую, но можно так: + getIt.registerSingletonAsync( + () async => UniversalServiceImpl( + value: depName, + dependency: level > 1 + ? await getIt.getAsync(instanceName: prevDepName) + : null, + ), + instanceName: depName, + ); + break; + } + } + } + break; + case UniversalScenario.override: + // handled at benchmark level + break; + } + }; + } + throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}'); +} diff --git a/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart b/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart index c006581..b4657c1 100644 --- a/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart +++ b/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart @@ -85,8 +85,8 @@ class UniversalChainModule extends Module { break; case UniversalScenario.named: // Named factory registration for two distinct objects. - bind().toProvide(() => UniversalServiceImpl(value: 'impl1')).withName('impl1'); - bind().toProvide(() => UniversalServiceImpl(value: 'impl2')).withName('impl2'); + bind().toProvide(() => UniversalServiceImpl(value: 'impl1')).withName('impl1'); + bind().toProvide(() => UniversalServiceImpl(value: 'impl2')).withName('impl2'); break; case UniversalScenario.chain: // Chain of nested services, with dependency on previous level by name. @@ -126,9 +126,18 @@ class UniversalChainModule extends Module { } } } + // Регистрация алиаса без имени (на последний элемент цепочки) + final depName = '${chainCount}_${nestingDepth}'; + bind() + .toProvide(() => currentScope.resolve(named: depName)) + .singleton(); break; case UniversalScenario.override: - // handled at benchmark level + // handled at benchmark level, но алиас нужен прямо в этом scope! + final depName = '${chainCount}_${nestingDepth}'; + bind() + .toProvide(() => currentScope.resolve(named: depName)) + .singleton(); break; case UniversalScenario.asyncChain: // already handled above diff --git a/benchmark_cherrypick/pubspec.lock b/benchmark_cherrypick/pubspec.lock index 2b5229b..24877a8 100644 --- a/benchmark_cherrypick/pubspec.lock +++ b/benchmark_cherrypick/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" benchmark_harness: dependency: "direct dev" description: @@ -40,6 +48,14 @@ packages: relative: true source: path version: "3.0.0-dev.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" exception_templates: dependency: transitive description: @@ -48,6 +64,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b + url: "https://pub.dev" + source: hosted + version: "8.2.0" lazy_memo: dependency: transitive description: diff --git a/benchmark_cherrypick/pubspec.yaml b/benchmark_cherrypick/pubspec.yaml index c3a3074..4dc437d 100644 --- a/benchmark_cherrypick/pubspec.yaml +++ b/benchmark_cherrypick/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: cherrypick: path: ../cherrypick args: ^2.7.0 + get_it: ^8.2.0 dev_dependencies: lints: ^5.0.0