mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-23 21:13:35 +00:00
fix: universal benchmarks and DI registration; proper named binding; robust override support for cherrypick and get_it; improved CLI args
This commit is contained in:
@@ -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<void> 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
|
||||
|
||||
@@ -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<UniversalService>();
|
||||
break;
|
||||
case UniversalScenario.named:
|
||||
_di.resolve<Object>(named: 'impl2');
|
||||
if (_di.runtimeType.toString().contains('GetItAdapter')) {
|
||||
_di.resolve<UniversalService>(named: 'impl2');
|
||||
} else {
|
||||
_di.resolve<UniversalService>(named: 'impl2');
|
||||
}
|
||||
break;
|
||||
case UniversalScenario.chain:
|
||||
final serviceName = '${chainCount}_$nestingDepth';
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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<String> 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<String> 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',
|
||||
);
|
||||
}
|
||||
@@ -12,11 +12,11 @@ class CherrypickDIAdapter implements DIAdapter {
|
||||
}
|
||||
|
||||
@override
|
||||
T resolve<T>({String? named}) =>
|
||||
T resolve<T extends Object>({String? named}) =>
|
||||
named == null ? _scope!.resolve<T>() : _scope!.resolve<T>(named: named);
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync<T>({String? named}) async =>
|
||||
Future<T> resolveAsync<T extends Object>({String? named}) async =>
|
||||
named == null ? await _scope!.resolveAsync<T>() : await _scope!.resolveAsync<T>(named: named);
|
||||
|
||||
@override
|
||||
@@ -30,6 +30,9 @@ class CherrypickDIAdapter implements DIAdapter {
|
||||
final sub = _scope!.openSubScope(name);
|
||||
return _CherrypickSubScopeAdapter(sub);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> waitForAsyncReady() async {}
|
||||
}
|
||||
|
||||
/// Internal adapter for a CherryPick sub-scope (callbacks based).
|
||||
@@ -43,11 +46,11 @@ class _CherrypickSubScopeAdapter extends CherrypickDIAdapter {
|
||||
}
|
||||
|
||||
@override
|
||||
T resolve<T>({String? named}) =>
|
||||
T resolve<T extends Object>({String? named}) =>
|
||||
named == null ? _subScope.resolve<T>() : _subScope.resolve<T>(named: named);
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync<T>({String? named}) async =>
|
||||
Future<T> resolveAsync<T extends Object>({String? named}) async =>
|
||||
named == null ? await _subScope.resolveAsync<T>() : await _subScope.resolveAsync<T>(named: named);
|
||||
|
||||
@override
|
||||
|
||||
@@ -8,14 +8,17 @@ abstract class DIAdapter {
|
||||
void setupDependencies(void Function(dynamic container) registration);
|
||||
|
||||
/// Резолвит (возвращает) экземпляр типа [T] (по имени, если требуется).
|
||||
T resolve<T>({String? named});
|
||||
T resolve<T extends Object>({String? named});
|
||||
|
||||
/// Асинхронно резолвит экземпляр типа [T].
|
||||
Future<T> resolveAsync<T>({String? named});
|
||||
Future<T> resolveAsync<T extends Object>({String? named});
|
||||
|
||||
/// Уничтожает/отчищает DI-контейнер.
|
||||
void teardown();
|
||||
|
||||
/// Открывает дочерний под-scope (если применимо).
|
||||
DIAdapter openSubScope(String name);
|
||||
|
||||
/// Ожидание готовности DI контейнера (нужно для async DI, например get_it)
|
||||
Future<void> waitForAsyncReady();
|
||||
}
|
||||
|
||||
32
benchmark_cherrypick/lib/di_adapters/get_it_adapter.dart
Normal file
32
benchmark_cherrypick/lib/di_adapters/get_it_adapter.dart
Normal file
@@ -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<T extends Object>({String? named}) => _getIt<T>(instanceName: named);
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync<T extends Object>({String? named}) async => _getIt<T>(instanceName: named);
|
||||
|
||||
@override
|
||||
void teardown() => _getIt.reset();
|
||||
|
||||
@override
|
||||
DIAdapter openSubScope(String name) {
|
||||
// get_it не поддерживает scope, возвращаем новый инстанс
|
||||
return GetItAdapter();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> waitForAsyncReady() async {
|
||||
await _getIt.allReady();
|
||||
}
|
||||
}
|
||||
@@ -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<UniversalService>(
|
||||
() async {
|
||||
final prev = level > 1
|
||||
? await getIt.getAsync<UniversalService>(instanceName: prevDepName)
|
||||
: null;
|
||||
return UniversalServiceImpl(value: depName, dependency: prev);
|
||||
},
|
||||
instanceName: depName,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UniversalScenario.register:
|
||||
getIt.registerSingleton<UniversalService>(UniversalServiceImpl(value: 'reg', dependency: null));
|
||||
break;
|
||||
case UniversalScenario.named:
|
||||
getIt.registerFactory<UniversalService>(() => UniversalServiceImpl(value: 'impl1'), instanceName: 'impl1');
|
||||
getIt.registerFactory<UniversalService>(() => 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<UniversalService>(
|
||||
UniversalServiceImpl(
|
||||
value: depName,
|
||||
dependency: level > 1
|
||||
? getIt<UniversalService>(instanceName: prevDepName)
|
||||
: null,
|
||||
),
|
||||
instanceName: depName,
|
||||
);
|
||||
break;
|
||||
case UniversalBindingMode.factoryStrategy:
|
||||
getIt.registerFactory<UniversalService>(
|
||||
() => UniversalServiceImpl(
|
||||
value: depName,
|
||||
dependency: level > 1
|
||||
? getIt<UniversalService>(instanceName: prevDepName)
|
||||
: null,
|
||||
),
|
||||
instanceName: depName,
|
||||
);
|
||||
break;
|
||||
case UniversalBindingMode.asyncStrategy:
|
||||
// getIt не поддерживает асинх. factory напрямую, но можно так:
|
||||
getIt.registerSingletonAsync<UniversalService>(
|
||||
() async => UniversalServiceImpl(
|
||||
value: depName,
|
||||
dependency: level > 1
|
||||
? await getIt.getAsync<UniversalService>(instanceName: prevDepName)
|
||||
: null,
|
||||
),
|
||||
instanceName: depName,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UniversalScenario.override:
|
||||
// handled at benchmark level
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}');
|
||||
}
|
||||
@@ -85,8 +85,8 @@ class UniversalChainModule extends Module {
|
||||
break;
|
||||
case UniversalScenario.named:
|
||||
// Named factory registration for two distinct objects.
|
||||
bind<Object>().toProvide(() => UniversalServiceImpl(value: 'impl1')).withName('impl1');
|
||||
bind<Object>().toProvide(() => UniversalServiceImpl(value: 'impl2')).withName('impl2');
|
||||
bind<UniversalService>().toProvide(() => UniversalServiceImpl(value: 'impl1')).withName('impl1');
|
||||
bind<UniversalService>().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<UniversalService>()
|
||||
.toProvide(() => currentScope.resolve<UniversalService>(named: depName))
|
||||
.singleton();
|
||||
break;
|
||||
case UniversalScenario.override:
|
||||
// handled at benchmark level
|
||||
// handled at benchmark level, но алиас нужен прямо в этом scope!
|
||||
final depName = '${chainCount}_${nestingDepth}';
|
||||
bind<UniversalService>()
|
||||
.toProvide(() => currentScope.resolve<UniversalService>(named: depName))
|
||||
.singleton();
|
||||
break;
|
||||
case UniversalScenario.asyncChain:
|
||||
// already handled above
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies:
|
||||
cherrypick:
|
||||
path: ../cherrypick
|
||||
args: ^2.7.0
|
||||
get_it: ^8.2.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
|
||||
Reference in New Issue
Block a user