mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-05-16 10:10:43 +00:00
feat(benchmark_di): add resolve phases, eager/lazy scenarios, and adapter parity
This commit is contained in:
@@ -25,7 +25,11 @@ class UniversalChainAsyncBenchmark<TContainer> extends AsyncBenchmarkBase {
|
|||||||
bindingMode: mode,
|
bindingMode: mode,
|
||||||
scenario: UniversalScenario.asyncChain,
|
scenario: UniversalScenario.asyncChain,
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> prewarm() async {
|
||||||
await di.waitForAsyncReady();
|
await di.waitForAsyncReady();
|
||||||
|
await run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -50,7 +50,14 @@ class UniversalChainBenchmark<TContainer> extends BenchmarkBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void teardown() => _di.teardown();
|
void teardown() {
|
||||||
|
_childDi?.teardown();
|
||||||
|
_di.teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void prewarm() {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void run() {
|
void run() {
|
||||||
@@ -59,11 +66,7 @@ class UniversalChainBenchmark<TContainer> extends BenchmarkBase {
|
|||||||
_di.resolve<UniversalService>();
|
_di.resolve<UniversalService>();
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.named:
|
case UniversalScenario.named:
|
||||||
if (_di.runtimeType.toString().contains('GetItAdapter')) {
|
|
||||||
_di.resolve<UniversalService>(named: 'impl2');
|
_di.resolve<UniversalService>(named: 'impl2');
|
||||||
} else {
|
|
||||||
_di.resolve<UniversalService>(named: 'impl2');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.chain:
|
case UniversalScenario.chain:
|
||||||
final serviceName = '${chainCount}_$nestingDepth';
|
final serviceName = '${chainCount}_$nestingDepth';
|
||||||
|
|||||||
@@ -31,9 +31,16 @@ class BenchmarkCliRunner {
|
|||||||
Future<void> run(List<String> args) async {
|
Future<void> run(List<String> args) async {
|
||||||
final config = parseBenchmarkCli(args);
|
final config = parseBenchmarkCli(args);
|
||||||
final results = <Map<String, dynamic>>[];
|
final results = <Map<String, dynamic>>[];
|
||||||
|
// DI implementations that do not support async scenarios
|
||||||
|
const asyncUnsupported = {'kiwi', 'yx_scope'};
|
||||||
|
for (final phase in config.phases) {
|
||||||
for (final bench in config.benchesToRun) {
|
for (final bench in config.benchesToRun) {
|
||||||
final scenario = toScenario(bench);
|
final scenario = toScenario(bench);
|
||||||
final mode = toMode(bench);
|
final mode = toMode(bench);
|
||||||
|
if (asyncUnsupported.contains(config.di) &&
|
||||||
|
scenario == UniversalScenario.asyncChain) {
|
||||||
|
continue; // Skip async benchmarks for DI that does not support them
|
||||||
|
}
|
||||||
for (final c in config.chainCounts) {
|
for (final c in config.chainCounts) {
|
||||||
for (final d in config.nestDepths) {
|
for (final d in config.nestDepths) {
|
||||||
BenchmarkResult benchResult;
|
BenchmarkResult benchResult;
|
||||||
@@ -50,6 +57,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchAsync,
|
benchmark: benchAsync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final benchSync = UniversalChainBenchmark<GetIt>(
|
final benchSync = UniversalChainBenchmark<GetIt>(
|
||||||
@@ -63,12 +71,12 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchSync,
|
benchmark: benchSync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (config.di == 'kiwi') {
|
} else if (config.di == 'kiwi') {
|
||||||
final di = KiwiAdapter();
|
final di = KiwiAdapter();
|
||||||
if (scenario == UniversalScenario.asyncChain) {
|
if (scenario == UniversalScenario.asyncChain) {
|
||||||
// UnsupportedError будет выброшен адаптером, но если дойдёт — вызывать async benchmark
|
|
||||||
final benchAsync = UniversalChainAsyncBenchmark<KiwiContainer>(
|
final benchAsync = UniversalChainAsyncBenchmark<KiwiContainer>(
|
||||||
di,
|
di,
|
||||||
chainCount: c,
|
chainCount: c,
|
||||||
@@ -79,6 +87,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchAsync,
|
benchmark: benchAsync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final benchSync = UniversalChainBenchmark<KiwiContainer>(
|
final benchSync = UniversalChainBenchmark<KiwiContainer>(
|
||||||
@@ -92,6 +101,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchSync,
|
benchmark: benchSync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (config.di == 'riverpod') {
|
} else if (config.di == 'riverpod') {
|
||||||
@@ -108,6 +118,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchAsync,
|
benchmark: benchAsync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final benchSync = UniversalChainBenchmark<
|
final benchSync = UniversalChainBenchmark<
|
||||||
@@ -122,6 +133,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchSync,
|
benchmark: benchSync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (config.di == 'yx_scope') {
|
} else if (config.di == 'yx_scope') {
|
||||||
@@ -138,6 +150,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchAsync,
|
benchmark: benchAsync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final benchSync =
|
final benchSync =
|
||||||
@@ -152,6 +165,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchSync,
|
benchmark: benchSync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -167,6 +181,7 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchAsync,
|
benchmark: benchAsync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final benchSync = UniversalChainBenchmark<Scope>(
|
final benchSync = UniversalChainBenchmark<Scope>(
|
||||||
@@ -180,22 +195,27 @@ class BenchmarkCliRunner {
|
|||||||
benchmark: benchSync,
|
benchmark: benchSync,
|
||||||
warmups: config.warmups,
|
warmups: config.warmups,
|
||||||
repeats: config.repeats,
|
repeats: config.repeats,
|
||||||
|
phase: phase,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final timings = benchResult.timings;
|
final timings = benchResult.timings;
|
||||||
|
if (timings.isEmpty) continue; // skip failed scenarios
|
||||||
timings.sort();
|
timings.sort();
|
||||||
var mean = timings.reduce((a, b) => a + b) / timings.length;
|
final count = timings.length;
|
||||||
var median = timings[timings.length ~/ 2];
|
final mean = timings.reduce((a, b) => a + b) / count;
|
||||||
var minVal = timings.first;
|
final median = count.isOdd
|
||||||
var maxVal = timings.last;
|
? timings[count ~/ 2]
|
||||||
var stddev = timings.isEmpty
|
: (timings[count ~/ 2 - 1] + timings[count ~/ 2]) / 2;
|
||||||
? 0
|
final minVal = timings.first;
|
||||||
: sqrt(
|
final maxVal = timings.last;
|
||||||
timings.map((x) => pow(x - mean, 2)).reduce((a, b) => a + b) /
|
final stddev = sqrt(timings
|
||||||
timings.length);
|
.map((x) => pow(x - mean, 2))
|
||||||
|
.reduce((a, b) => a + b) /
|
||||||
|
count);
|
||||||
results.add({
|
results.add({
|
||||||
'benchmark': 'Universal_$bench',
|
'benchmark': 'Universal_$bench',
|
||||||
|
'phase': phase.name,
|
||||||
'chainCount': c,
|
'chainCount': c,
|
||||||
'nestingDepth': d,
|
'nestingDepth': d,
|
||||||
'mean_us': mean.toStringAsFixed(2),
|
'mean_us': mean.toStringAsFixed(2),
|
||||||
@@ -212,6 +232,7 @@ class BenchmarkCliRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final reportGenerators = {
|
final reportGenerators = {
|
||||||
'pretty': PrettyReport(),
|
'pretty': PrettyReport(),
|
||||||
'csv': CsvReport(),
|
'csv': CsvReport(),
|
||||||
|
|||||||
@@ -6,12 +6,18 @@ import 'package:benchmark_di/scenarios/universal_scenario.dart';
|
|||||||
|
|
||||||
/// Enum describing all supported Universal DI benchmark types.
|
/// Enum describing all supported Universal DI benchmark types.
|
||||||
enum UniversalBenchmark {
|
enum UniversalBenchmark {
|
||||||
/// Simple singleton registration benchmark
|
/// Simple singleton registration benchmark (eager, where supported)
|
||||||
registerSingleton,
|
registerSingleton,
|
||||||
|
|
||||||
/// Chain of singleton dependencies
|
/// Simple lazy singleton registration benchmark
|
||||||
|
registerLazySingleton,
|
||||||
|
|
||||||
|
/// Chain of eager singleton dependencies
|
||||||
chainSingleton,
|
chainSingleton,
|
||||||
|
|
||||||
|
/// Chain of lazy singleton dependencies
|
||||||
|
chainLazySingleton,
|
||||||
|
|
||||||
/// Chain using factories
|
/// Chain using factories
|
||||||
chainFactory,
|
chainFactory,
|
||||||
|
|
||||||
@@ -25,12 +31,19 @@ enum UniversalBenchmark {
|
|||||||
override,
|
override,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ResolvePhase {
|
||||||
|
firstResolve,
|
||||||
|
steadyStateResolve,
|
||||||
|
}
|
||||||
|
|
||||||
/// Maps [UniversalBenchmark] to the scenario enum for DI chains.
|
/// Maps [UniversalBenchmark] to the scenario enum for DI chains.
|
||||||
UniversalScenario toScenario(UniversalBenchmark b) {
|
UniversalScenario toScenario(UniversalBenchmark b) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case UniversalBenchmark.registerSingleton:
|
case UniversalBenchmark.registerSingleton:
|
||||||
|
case UniversalBenchmark.registerLazySingleton:
|
||||||
return UniversalScenario.register;
|
return UniversalScenario.register;
|
||||||
case UniversalBenchmark.chainSingleton:
|
case UniversalBenchmark.chainSingleton:
|
||||||
|
case UniversalBenchmark.chainLazySingleton:
|
||||||
return UniversalScenario.chain;
|
return UniversalScenario.chain;
|
||||||
case UniversalBenchmark.chainFactory:
|
case UniversalBenchmark.chainFactory:
|
||||||
return UniversalScenario.chain;
|
return UniversalScenario.chain;
|
||||||
@@ -43,21 +56,21 @@ UniversalScenario toScenario(UniversalBenchmark b) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps benchmark to registration mode (singleton/factory/async).
|
/// Maps benchmark to registration mode (singleton/lazySingleton/factory/async).
|
||||||
UniversalBindingMode toMode(UniversalBenchmark b) {
|
UniversalBindingMode toMode(UniversalBenchmark b) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case UniversalBenchmark.registerSingleton:
|
case UniversalBenchmark.registerSingleton:
|
||||||
return UniversalBindingMode.singletonStrategy;
|
|
||||||
case UniversalBenchmark.chainSingleton:
|
case UniversalBenchmark.chainSingleton:
|
||||||
|
case UniversalBenchmark.named:
|
||||||
|
case UniversalBenchmark.override:
|
||||||
return UniversalBindingMode.singletonStrategy;
|
return UniversalBindingMode.singletonStrategy;
|
||||||
|
case UniversalBenchmark.registerLazySingleton:
|
||||||
|
case UniversalBenchmark.chainLazySingleton:
|
||||||
|
return UniversalBindingMode.lazySingletonStrategy;
|
||||||
case UniversalBenchmark.chainFactory:
|
case UniversalBenchmark.chainFactory:
|
||||||
return UniversalBindingMode.factoryStrategy;
|
return UniversalBindingMode.factoryStrategy;
|
||||||
case UniversalBenchmark.chainAsync:
|
case UniversalBenchmark.chainAsync:
|
||||||
return UniversalBindingMode.asyncStrategy;
|
return UniversalBindingMode.asyncStrategy;
|
||||||
case UniversalBenchmark.named:
|
|
||||||
return UniversalBindingMode.singletonStrategy;
|
|
||||||
case UniversalBenchmark.override:
|
|
||||||
return UniversalBindingMode.singletonStrategy;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +111,10 @@ class BenchmarkCliConfig {
|
|||||||
|
|
||||||
/// Name of DI implementation ("cherrypick" or "getit")
|
/// Name of DI implementation ("cherrypick" or "getit")
|
||||||
final String di;
|
final String di;
|
||||||
|
|
||||||
|
/// Which resolve phase(s) to measure.
|
||||||
|
final List<ResolvePhase> phases;
|
||||||
|
|
||||||
BenchmarkCliConfig({
|
BenchmarkCliConfig({
|
||||||
required this.benchesToRun,
|
required this.benchesToRun,
|
||||||
required this.chainCounts,
|
required this.chainCounts,
|
||||||
@@ -106,6 +123,7 @@ class BenchmarkCliConfig {
|
|||||||
required this.warmups,
|
required this.warmups,
|
||||||
required this.format,
|
required this.format,
|
||||||
required this.di,
|
required this.di,
|
||||||
|
required this.phases,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +137,8 @@ BenchmarkCliConfig parseBenchmarkCli(List<String> args) {
|
|||||||
..addOption('repeat', abbr: 'r', defaultsTo: '2')
|
..addOption('repeat', abbr: 'r', defaultsTo: '2')
|
||||||
..addOption('warmup', abbr: 'w', defaultsTo: '1')
|
..addOption('warmup', abbr: 'w', defaultsTo: '1')
|
||||||
..addOption('format', abbr: 'f', defaultsTo: 'pretty')
|
..addOption('format', abbr: 'f', defaultsTo: 'pretty')
|
||||||
|
..addOption('resolvePhase',
|
||||||
|
defaultsTo: 'all', help: 'Resolve phase: first, steady, or all')
|
||||||
..addOption('di',
|
..addOption('di',
|
||||||
defaultsTo: 'cherrypick',
|
defaultsTo: 'cherrypick',
|
||||||
help: 'DI implementation: cherrypick, getit or riverpod')
|
help: 'DI implementation: cherrypick, getit or riverpod')
|
||||||
@@ -128,12 +148,39 @@ BenchmarkCliConfig parseBenchmarkCli(List<String> args) {
|
|||||||
print(parser.usage);
|
print(parser.usage);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
final benchName = result['benchmark'] as String;
|
final benchNameInput = result['benchmark'] as String;
|
||||||
final isAll = benchName == 'all';
|
final isAll = benchNameInput.trim() == 'all';
|
||||||
final allBenches = UniversalBenchmark.values;
|
final allBenches = UniversalBenchmark.values;
|
||||||
|
|
||||||
|
String normalizeBenchName(String name) {
|
||||||
|
final n = name.trim().toLowerCase();
|
||||||
|
return switch (n) {
|
||||||
|
'register' || 'registersingleton' || 'registereager' => 'registerSingleton',
|
||||||
|
'registerlazy' || 'registerlazysingleton' || 'registerlazysingle' => 'registerLazySingleton',
|
||||||
|
'chain' || 'chainsingleton' || 'chaineager' => 'chainSingleton',
|
||||||
|
'chainlazy' || 'chainlazysingleton' || 'lazysingleton' => 'chainLazySingleton',
|
||||||
|
'chainfactory' || 'factory' => 'chainFactory',
|
||||||
|
'async' || 'asyncchain' || 'chainasync' => 'chainAsync',
|
||||||
|
'named' => 'named',
|
||||||
|
'override' => 'override',
|
||||||
|
_ => n,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
final benchesToRun = isAll
|
final benchesToRun = isAll
|
||||||
? allBenches
|
? allBenches
|
||||||
: [parseEnum(benchName, allBenches, UniversalBenchmark.chainSingleton)];
|
: benchNameInput
|
||||||
|
.split(',')
|
||||||
|
.map((n) => parseEnum(normalizeBenchName(n), allBenches,
|
||||||
|
UniversalBenchmark.chainSingleton))
|
||||||
|
.toSet()
|
||||||
|
.toList();
|
||||||
|
final phaseName = (result['resolvePhase'] as String).toLowerCase();
|
||||||
|
final phases = switch (phaseName) {
|
||||||
|
'first' => [ResolvePhase.firstResolve],
|
||||||
|
'steady' => [ResolvePhase.steadyStateResolve],
|
||||||
|
_ => ResolvePhase.values,
|
||||||
|
};
|
||||||
return BenchmarkCliConfig(
|
return BenchmarkCliConfig(
|
||||||
benchesToRun: benchesToRun,
|
benchesToRun: benchesToRun,
|
||||||
chainCounts: parseIntList(result['chainCount'] as String),
|
chainCounts: parseIntList(result['chainCount'] as String),
|
||||||
@@ -142,5 +189,6 @@ BenchmarkCliConfig parseBenchmarkCli(List<String> args) {
|
|||||||
warmups: int.tryParse(result['warmup'] as String? ?? "") ?? 1,
|
warmups: int.tryParse(result['warmup'] as String? ?? "") ?? 1,
|
||||||
format: result['format'] as String,
|
format: result['format'] as String,
|
||||||
di: result['di'] as String? ?? 'cherrypick',
|
di: result['di'] as String? ?? 'cherrypick',
|
||||||
|
phases: phases,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class CsvReport extends ReportGenerator {
|
|||||||
@override
|
@override
|
||||||
final List<String> keys = [
|
final List<String> keys = [
|
||||||
'benchmark',
|
'benchmark',
|
||||||
|
'phase',
|
||||||
'chainCount',
|
'chainCount',
|
||||||
'nestingDepth',
|
'nestingDepth',
|
||||||
'mean_us',
|
'mean_us',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class MarkdownReport extends ReportGenerator {
|
|||||||
@override
|
@override
|
||||||
final List<String> keys = [
|
final List<String> keys = [
|
||||||
'benchmark',
|
'benchmark',
|
||||||
|
'phase',
|
||||||
'chainCount',
|
'chainCount',
|
||||||
'nestingDepth',
|
'nestingDepth',
|
||||||
'mean_us',
|
'mean_us',
|
||||||
@@ -24,7 +25,9 @@ class MarkdownReport extends ReportGenerator {
|
|||||||
/// Friendly display names for each benchmark type.
|
/// Friendly display names for each benchmark type.
|
||||||
static const nameMap = {
|
static const nameMap = {
|
||||||
'Universal_UniversalBenchmark.registerSingleton': 'RegisterSingleton',
|
'Universal_UniversalBenchmark.registerSingleton': 'RegisterSingleton',
|
||||||
|
'Universal_UniversalBenchmark.registerLazySingleton': 'RegisterLazySingleton',
|
||||||
'Universal_UniversalBenchmark.chainSingleton': 'ChainSingleton',
|
'Universal_UniversalBenchmark.chainSingleton': 'ChainSingleton',
|
||||||
|
'Universal_UniversalBenchmark.chainLazySingleton': 'ChainLazySingleton',
|
||||||
'Universal_UniversalBenchmark.chainFactory': 'ChainFactory',
|
'Universal_UniversalBenchmark.chainFactory': 'ChainFactory',
|
||||||
'Universal_UniversalBenchmark.chainAsync': 'AsyncChain',
|
'Universal_UniversalBenchmark.chainAsync': 'AsyncChain',
|
||||||
'Universal_UniversalBenchmark.named': 'Named',
|
'Universal_UniversalBenchmark.named': 'Named',
|
||||||
@@ -36,6 +39,7 @@ class MarkdownReport extends ReportGenerator {
|
|||||||
String render(List<Map<String, dynamic>> rows) {
|
String render(List<Map<String, dynamic>> rows) {
|
||||||
final headers = [
|
final headers = [
|
||||||
'Benchmark',
|
'Benchmark',
|
||||||
|
'Phase',
|
||||||
'Chain Count',
|
'Chain Count',
|
||||||
'Depth',
|
'Depth',
|
||||||
'Mean (us)',
|
'Mean (us)',
|
||||||
@@ -52,6 +56,7 @@ class MarkdownReport extends ReportGenerator {
|
|||||||
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
|
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
|
||||||
return [
|
return [
|
||||||
readableName,
|
readableName,
|
||||||
|
r['phase'],
|
||||||
r['chainCount'],
|
r['chainCount'],
|
||||||
r['nestingDepth'],
|
r['nestingDepth'],
|
||||||
r['mean_us'],
|
r['mean_us'],
|
||||||
@@ -82,6 +87,7 @@ class MarkdownReport extends ReportGenerator {
|
|||||||
final legend = '''
|
final legend = '''
|
||||||
> **Legend:**
|
> **Legend:**
|
||||||
> `Benchmark` – Test name
|
> `Benchmark` – Test name
|
||||||
|
> `Phase` – `firstResolve` or `steadyStateResolve`
|
||||||
> `Chain Count` – Number of independent chains
|
> `Chain Count` – Number of independent chains
|
||||||
> `Depth` – Depth of each chain
|
> `Depth` – Depth of each chain
|
||||||
> `Mean (us)` – Average time per run (microseconds)
|
> `Mean (us)` – Average time per run (microseconds)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class PrettyReport extends ReportGenerator {
|
|||||||
@override
|
@override
|
||||||
final List<String> keys = [
|
final List<String> keys = [
|
||||||
'benchmark',
|
'benchmark',
|
||||||
|
'phase',
|
||||||
'chainCount',
|
'chainCount',
|
||||||
'nestingDepth',
|
'nestingDepth',
|
||||||
'mean_us',
|
'mean_us',
|
||||||
@@ -24,7 +25,9 @@ class PrettyReport extends ReportGenerator {
|
|||||||
/// Mappings from internal benchmark IDs to display names.
|
/// Mappings from internal benchmark IDs to display names.
|
||||||
static const nameMap = {
|
static const nameMap = {
|
||||||
'Universal_UniversalBenchmark.registerSingleton': 'RegisterSingleton',
|
'Universal_UniversalBenchmark.registerSingleton': 'RegisterSingleton',
|
||||||
|
'Universal_UniversalBenchmark.registerLazySingleton': 'RegisterLazySingleton',
|
||||||
'Universal_UniversalBenchmark.chainSingleton': 'ChainSingleton',
|
'Universal_UniversalBenchmark.chainSingleton': 'ChainSingleton',
|
||||||
|
'Universal_UniversalBenchmark.chainLazySingleton': 'ChainLazySingleton',
|
||||||
'Universal_UniversalBenchmark.chainFactory': 'ChainFactory',
|
'Universal_UniversalBenchmark.chainFactory': 'ChainFactory',
|
||||||
'Universal_UniversalBenchmark.chainAsync': 'AsyncChain',
|
'Universal_UniversalBenchmark.chainAsync': 'AsyncChain',
|
||||||
'Universal_UniversalBenchmark.named': 'Named',
|
'Universal_UniversalBenchmark.named': 'Named',
|
||||||
@@ -36,6 +39,7 @@ class PrettyReport extends ReportGenerator {
|
|||||||
String render(List<Map<String, dynamic>> rows) {
|
String render(List<Map<String, dynamic>> rows) {
|
||||||
final headers = [
|
final headers = [
|
||||||
'Benchmark',
|
'Benchmark',
|
||||||
|
'Phase',
|
||||||
'Chain Count',
|
'Chain Count',
|
||||||
'Depth',
|
'Depth',
|
||||||
'Mean (us)',
|
'Mean (us)',
|
||||||
@@ -53,6 +57,7 @@ class PrettyReport extends ReportGenerator {
|
|||||||
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
|
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
|
||||||
return [
|
return [
|
||||||
readableName,
|
readableName,
|
||||||
|
r['phase'],
|
||||||
r['chainCount'],
|
r['chainCount'],
|
||||||
r['nestingDepth'],
|
r['nestingDepth'],
|
||||||
r['mean_us'],
|
r['mean_us'],
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:benchmark_di/benchmarks/universal_chain_benchmark.dart';
|
import 'package:benchmark_di/benchmarks/universal_chain_benchmark.dart';
|
||||||
import 'package:benchmark_di/benchmarks/universal_chain_async_benchmark.dart';
|
import 'package:benchmark_di/benchmarks/universal_chain_async_benchmark.dart';
|
||||||
|
import 'package:benchmark_di/cli/parser.dart';
|
||||||
|
|
||||||
/// Holds the results for a single benchmark execution.
|
/// Holds the results for a single benchmark execution.
|
||||||
class BenchmarkResult {
|
class BenchmarkResult {
|
||||||
@@ -50,17 +51,24 @@ class BenchmarkRunner {
|
|||||||
required UniversalChainBenchmark benchmark,
|
required UniversalChainBenchmark benchmark,
|
||||||
required int warmups,
|
required int warmups,
|
||||||
required int repeats,
|
required int repeats,
|
||||||
|
required ResolvePhase phase,
|
||||||
}) async {
|
}) async {
|
||||||
final timings = <num>[];
|
final timings = <num>[];
|
||||||
final rssValues = <int>[];
|
final rssValues = <int>[];
|
||||||
for (int i = 0; i < warmups; i++) {
|
for (int i = 0; i < warmups; i++) {
|
||||||
benchmark.setup();
|
benchmark.setup();
|
||||||
|
if (phase == ResolvePhase.steadyStateResolve) {
|
||||||
|
benchmark.prewarm();
|
||||||
|
}
|
||||||
benchmark.run();
|
benchmark.run();
|
||||||
benchmark.teardown();
|
benchmark.teardown();
|
||||||
}
|
}
|
||||||
final memBefore = ProcessInfo.currentRss;
|
final memBefore = ProcessInfo.currentRss;
|
||||||
for (int i = 0; i < repeats; i++) {
|
for (int i = 0; i < repeats; i++) {
|
||||||
benchmark.setup();
|
benchmark.setup();
|
||||||
|
if (phase == ResolvePhase.steadyStateResolve) {
|
||||||
|
benchmark.prewarm();
|
||||||
|
}
|
||||||
final sw = Stopwatch()..start();
|
final sw = Stopwatch()..start();
|
||||||
benchmark.run();
|
benchmark.run();
|
||||||
sw.stop();
|
sw.stop();
|
||||||
@@ -78,17 +86,24 @@ class BenchmarkRunner {
|
|||||||
required UniversalChainAsyncBenchmark benchmark,
|
required UniversalChainAsyncBenchmark benchmark,
|
||||||
required int warmups,
|
required int warmups,
|
||||||
required int repeats,
|
required int repeats,
|
||||||
|
required ResolvePhase phase,
|
||||||
}) async {
|
}) async {
|
||||||
final timings = <num>[];
|
final timings = <num>[];
|
||||||
final rssValues = <int>[];
|
final rssValues = <int>[];
|
||||||
for (int i = 0; i < warmups; i++) {
|
for (int i = 0; i < warmups; i++) {
|
||||||
await benchmark.setup();
|
await benchmark.setup();
|
||||||
|
if (phase == ResolvePhase.steadyStateResolve) {
|
||||||
|
await benchmark.prewarm();
|
||||||
|
}
|
||||||
await benchmark.run();
|
await benchmark.run();
|
||||||
await benchmark.teardown();
|
await benchmark.teardown();
|
||||||
}
|
}
|
||||||
final memBefore = ProcessInfo.currentRss;
|
final memBefore = ProcessInfo.currentRss;
|
||||||
for (int i = 0; i < repeats; i++) {
|
for (int i = 0; i < repeats; i++) {
|
||||||
await benchmark.setup();
|
await benchmark.setup();
|
||||||
|
if (phase == ResolvePhase.steadyStateResolve) {
|
||||||
|
await benchmark.prewarm();
|
||||||
|
}
|
||||||
final sw = Stopwatch()..start();
|
final sw = Stopwatch()..start();
|
||||||
await benchmark.run();
|
await benchmark.run();
|
||||||
sw.stop();
|
sw.stop();
|
||||||
|
|||||||
@@ -59,11 +59,16 @@ class UniversalChainModule extends Module {
|
|||||||
|
|
||||||
switch (scenario) {
|
switch (scenario) {
|
||||||
case UniversalScenario.register:
|
case UniversalScenario.register:
|
||||||
// Simple singleton registration.
|
if (bindingMode == UniversalBindingMode.lazySingletonStrategy) {
|
||||||
bind<UniversalService>()
|
bind<UniversalService>()
|
||||||
.toProvide(
|
.toProvide(
|
||||||
() => UniversalServiceImpl(value: 'reg', dependency: null))
|
() => UniversalServiceImpl(value: 'reg', dependency: null))
|
||||||
.singleton();
|
.singleton();
|
||||||
|
} else {
|
||||||
|
bind<UniversalService>()
|
||||||
|
.toInstance(
|
||||||
|
UniversalServiceImpl(value: 'reg', dependency: null));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.named:
|
case UniversalScenario.named:
|
||||||
// Named factory registration for two distinct objects.
|
// Named factory registration for two distinct objects.
|
||||||
@@ -76,6 +81,8 @@ class UniversalChainModule extends Module {
|
|||||||
break;
|
break;
|
||||||
case UniversalScenario.chain:
|
case UniversalScenario.chain:
|
||||||
// Chain of nested services, with dependency on previous level by name.
|
// Chain of nested services, with dependency on previous level by name.
|
||||||
|
UniversalService? lastEagerInstance;
|
||||||
|
final Map<String, UniversalService> eagerInstances = {};
|
||||||
for (var chainIndex = 0; chainIndex < chainCount; chainIndex++) {
|
for (var chainIndex = 0; chainIndex < chainCount; chainIndex++) {
|
||||||
for (var levelIndex = 0; levelIndex < nestingDepth; levelIndex++) {
|
for (var levelIndex = 0; levelIndex < nestingDepth; levelIndex++) {
|
||||||
final chain = chainIndex + 1;
|
final chain = chainIndex + 1;
|
||||||
@@ -84,6 +91,17 @@ class UniversalChainModule extends Module {
|
|||||||
final depName = '${chain}_$level';
|
final depName = '${chain}_$level';
|
||||||
switch (bindingMode) {
|
switch (bindingMode) {
|
||||||
case UniversalBindingMode.singletonStrategy:
|
case UniversalBindingMode.singletonStrategy:
|
||||||
|
final instance = UniversalServiceImpl(
|
||||||
|
value: depName,
|
||||||
|
dependency: eagerInstances[prevDepName],
|
||||||
|
);
|
||||||
|
bind<UniversalService>()
|
||||||
|
.toInstance(instance)
|
||||||
|
.withName(depName);
|
||||||
|
eagerInstances[depName] = instance;
|
||||||
|
lastEagerInstance = instance;
|
||||||
|
break;
|
||||||
|
case UniversalBindingMode.lazySingletonStrategy:
|
||||||
bind<UniversalService>()
|
bind<UniversalService>()
|
||||||
.toProvide(() => UniversalServiceImpl(
|
.toProvide(() => UniversalServiceImpl(
|
||||||
value: depName,
|
value: depName,
|
||||||
@@ -116,15 +134,19 @@ class UniversalChainModule extends Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Регистрация алиаса без имени (на последний элемент цепочки)
|
// Register unnamed alias for the last chain element.
|
||||||
|
if (bindingMode == UniversalBindingMode.lazySingletonStrategy) {
|
||||||
final depName = '${chainCount}_$nestingDepth';
|
final depName = '${chainCount}_$nestingDepth';
|
||||||
bind<UniversalService>()
|
bind<UniversalService>()
|
||||||
.toProvide(
|
.toProvide(
|
||||||
() => currentScope.resolve<UniversalService>(named: depName))
|
() => currentScope.resolve<UniversalService>(named: depName))
|
||||||
.singleton();
|
.singleton();
|
||||||
|
} else if (lastEagerInstance != null) {
|
||||||
|
bind<UniversalService>().toInstance(lastEagerInstance);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.override:
|
case UniversalScenario.override:
|
||||||
// handled at benchmark level, но алиас нужен прямо в этом scope!
|
// Handled at benchmark level, but alias is needed directly in this scope.
|
||||||
final depName = '${chainCount}_$nestingDepth';
|
final depName = '${chainCount}_$nestingDepth';
|
||||||
bind<UniversalService>()
|
bind<UniversalService>()
|
||||||
.toProvide(
|
.toProvide(
|
||||||
@@ -189,7 +211,7 @@ class CherrypickDIAdapter extends DIAdapter<Scope> {
|
|||||||
await CherryPick.closeRootScope();
|
await CherryPick.closeRootScope();
|
||||||
_scope = null;
|
_scope = null;
|
||||||
}
|
}
|
||||||
// SubScope teardown не требуется
|
// SubScope teardown is handled by the parent scope.
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import 'package:benchmark_di/scenarios/universal_service.dart';
|
|||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'di_adapter.dart';
|
import 'di_adapter.dart';
|
||||||
|
|
||||||
/// Универсальный DIAdapter для GetIt c поддержкой scopes и строгой типизацией.
|
/// DIAdapter for GetIt with scope support and strict typing.
|
||||||
class GetItAdapter extends DIAdapter<GetIt> {
|
class GetItAdapter extends DIAdapter<GetIt> {
|
||||||
late GetIt _getIt;
|
late GetIt _getIt;
|
||||||
final String? _scopeName;
|
final String? _scopeName;
|
||||||
final bool _isSubScope;
|
final bool _isSubScope;
|
||||||
bool _scopePushed = false;
|
bool _scopePushed = false;
|
||||||
|
|
||||||
/// Основной (root) и subScope-конструкторы.
|
/// Root and subScope constructors.
|
||||||
GetItAdapter({GetIt? instance, String? scopeName, bool isSubScope = false})
|
GetItAdapter({GetIt? instance, String? scopeName, bool isSubScope = false})
|
||||||
: _scopeName = scopeName,
|
: _scopeName = scopeName,
|
||||||
_isSubScope = isSubScope {
|
_isSubScope = isSubScope {
|
||||||
@@ -23,7 +23,7 @@ class GetItAdapter extends DIAdapter<GetIt> {
|
|||||||
@override
|
@override
|
||||||
void setupDependencies(void Function(GetIt container) registration) {
|
void setupDependencies(void Function(GetIt container) registration) {
|
||||||
if (_isSubScope) {
|
if (_isSubScope) {
|
||||||
// Создаём scope через pushNewScope с init
|
// Create scope via pushNewScope with init
|
||||||
_getIt.pushNewScope(
|
_getIt.pushNewScope(
|
||||||
scopeName: _scopeName,
|
scopeName: _scopeName,
|
||||||
init: (getIt) => registration(getIt),
|
init: (getIt) => registration(getIt),
|
||||||
@@ -41,7 +41,7 @@ class GetItAdapter extends DIAdapter<GetIt> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> resolveAsync<T extends Object>({String? named}) async =>
|
Future<T> resolveAsync<T extends Object>({String? named}) async =>
|
||||||
_getIt<T>(instanceName: named);
|
_getIt.getAsync<T>(instanceName: named);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void teardown() {
|
void teardown() {
|
||||||
@@ -92,8 +92,13 @@ class GetItAdapter extends DIAdapter<GetIt> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.register:
|
case UniversalScenario.register:
|
||||||
|
if (bindingMode == UniversalBindingMode.lazySingletonStrategy) {
|
||||||
|
getIt.registerLazySingleton<UniversalService>(
|
||||||
|
() => UniversalServiceImpl(value: 'reg', dependency: null));
|
||||||
|
} else {
|
||||||
getIt.registerSingleton<UniversalService>(
|
getIt.registerSingleton<UniversalService>(
|
||||||
UniversalServiceImpl(value: 'reg', dependency: null));
|
UniversalServiceImpl(value: 'reg', dependency: null));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UniversalScenario.named:
|
case UniversalScenario.named:
|
||||||
getIt.registerFactory<UniversalService>(
|
getIt.registerFactory<UniversalService>(
|
||||||
@@ -120,6 +125,17 @@ class GetItAdapter extends DIAdapter<GetIt> {
|
|||||||
instanceName: depName,
|
instanceName: depName,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case UniversalBindingMode.lazySingletonStrategy:
|
||||||
|
getIt.registerLazySingleton<UniversalService>(
|
||||||
|
() => UniversalServiceImpl(
|
||||||
|
value: depName,
|
||||||
|
dependency: level > 1
|
||||||
|
? getIt<UniversalService>(instanceName: prevDepName)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
instanceName: depName,
|
||||||
|
);
|
||||||
|
break;
|
||||||
case UniversalBindingMode.factoryStrategy:
|
case UniversalBindingMode.factoryStrategy:
|
||||||
getIt.registerFactory<UniversalService>(
|
getIt.registerFactory<UniversalService>(
|
||||||
() => UniversalServiceImpl(
|
() => UniversalServiceImpl(
|
||||||
@@ -154,10 +170,16 @@ class GetItAdapter extends DIAdapter<GetIt> {
|
|||||||
if (scenario == UniversalScenario.chain ||
|
if (scenario == UniversalScenario.chain ||
|
||||||
scenario == UniversalScenario.override) {
|
scenario == UniversalScenario.override) {
|
||||||
final depName = '${chainCount}_$nestingDepth';
|
final depName = '${chainCount}_$nestingDepth';
|
||||||
|
if (bindingMode == UniversalBindingMode.lazySingletonStrategy) {
|
||||||
|
getIt.registerLazySingleton<UniversalService>(
|
||||||
|
() => getIt<UniversalService>(instanceName: depName),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
getIt.registerSingleton<UniversalService>(
|
getIt.registerSingleton<UniversalService>(
|
||||||
getIt<UniversalService>(instanceName: depName),
|
getIt<UniversalService>(instanceName: depName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw UnsupportedError('Scenario $scenario not supported by GetItAdapter');
|
throw UnsupportedError('Scenario $scenario not supported by GetItAdapter');
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import 'package:benchmark_di/scenarios/universal_service.dart';
|
|||||||
import 'package:kiwi/kiwi.dart';
|
import 'package:kiwi/kiwi.dart';
|
||||||
import 'di_adapter.dart';
|
import 'di_adapter.dart';
|
||||||
|
|
||||||
/// DIAdapter-для KiwiContainer с поддержкой universal benchmark сценариев.
|
/// DIAdapter for KiwiContainer with universal benchmark scenario support.
|
||||||
class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
||||||
late KiwiContainer _container;
|
late KiwiContainer _container;
|
||||||
// ignore: unused_field
|
|
||||||
final bool _isSubScope;
|
|
||||||
|
|
||||||
KiwiAdapter({KiwiContainer? container, bool isSubScope = false})
|
KiwiAdapter({KiwiContainer? container, bool isSubScope = false}) {
|
||||||
: _isSubScope = isSubScope {
|
|
||||||
_container = container ?? KiwiContainer();
|
_container = container ?? KiwiContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +54,8 @@ class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
|||||||
final depName = '${chain}_$level';
|
final depName = '${chain}_$level';
|
||||||
switch (bindingMode) {
|
switch (bindingMode) {
|
||||||
case UniversalBindingMode.singletonStrategy:
|
case UniversalBindingMode.singletonStrategy:
|
||||||
|
case UniversalBindingMode.lazySingletonStrategy:
|
||||||
|
// Kiwi's registerSingleton is lazy (factory-based, cached on first resolve)
|
||||||
container.registerSingleton<UniversalService>(
|
container.registerSingleton<UniversalService>(
|
||||||
(c) => UniversalServiceImpl(
|
(c) => UniversalServiceImpl(
|
||||||
value: depName,
|
value: depName,
|
||||||
@@ -75,7 +74,7 @@ class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
|||||||
name: depName);
|
name: depName);
|
||||||
break;
|
break;
|
||||||
case UniversalBindingMode.asyncStrategy:
|
case UniversalBindingMode.asyncStrategy:
|
||||||
// Не поддерживается
|
// Not supported
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,23 +96,12 @@ class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
T resolve<T extends Object>({String? named}) {
|
T resolve<T extends Object>({String? named}) {
|
||||||
// Для asyncChain нужен resolve<Future<T>>
|
|
||||||
if (T.toString().startsWith('Future<')) {
|
|
||||||
return _container.resolve<T>(named);
|
return _container.resolve<T>(named);
|
||||||
} else {
|
|
||||||
return _container.resolve<T>(named);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> resolveAsync<T extends Object>({String? named}) async {
|
Future<T> resolveAsync<T extends Object>({String? named}) {
|
||||||
if (T.toString().startsWith('Future<')) {
|
|
||||||
// resolve<Future<T>>, unwrap result
|
|
||||||
return Future.value(_container.resolve<T>(named));
|
return Future.value(_container.resolve<T>(named));
|
||||||
} else {
|
|
||||||
// Для совместимости с chain/override
|
|
||||||
return Future.value(_container.resolve<T>(named));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -123,7 +111,7 @@ class KiwiAdapter extends DIAdapter<KiwiContainer> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
KiwiAdapter openSubScope(String name) {
|
KiwiAdapter openSubScope(String name) {
|
||||||
// Возвращаем новый scoped контейнер (отдельный). Наследование не реализовано.
|
// Returns a new scoped container. Inheritance not implemented.
|
||||||
return KiwiAdapter(container: KiwiContainer.scoped(), isSubScope: true);
|
return KiwiAdapter(container: KiwiContainer.scoped(), isSubScope: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ class YxScopeAdapter extends DIAdapter<UniversalYxScopeContainer> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void teardown() {
|
void teardown() {
|
||||||
// У yx_scope нет явного dispose на ScopeContainer, но можно добавить очистку Map/Deps если потребуется
|
_scope = UniversalYxScopeContainer();
|
||||||
// Ничего не делаем
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,26 +53,13 @@ class YxScopeAdapter extends DIAdapter<UniversalYxScopeContainer> {
|
|||||||
required UniversalBindingMode bindingMode,
|
required UniversalBindingMode bindingMode,
|
||||||
}) {
|
}) {
|
||||||
if (scenario is UniversalScenario) {
|
if (scenario is UniversalScenario) {
|
||||||
|
if (scenario == UniversalScenario.asyncChain ||
|
||||||
|
bindingMode == UniversalBindingMode.asyncStrategy) {
|
||||||
|
throw UnsupportedError(
|
||||||
|
'YxScope does not support async dependencies or async binding scenarios.');
|
||||||
|
}
|
||||||
return (scope) {
|
return (scope) {
|
||||||
switch (scenario) {
|
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';
|
|
||||||
final dep = scope.dep<UniversalService>(
|
|
||||||
() => UniversalServiceImpl(
|
|
||||||
value: depName,
|
|
||||||
dependency: level > 1
|
|
||||||
? scope.depFor<UniversalService>(name: prevDepName).get
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
name: depName,
|
|
||||||
);
|
|
||||||
scope.register<UniversalService>(dep, name: depName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case UniversalScenario.register:
|
case UniversalScenario.register:
|
||||||
final dep = scope.dep<UniversalService>(
|
final dep = scope.dep<UniversalService>(
|
||||||
() => UniversalServiceImpl(value: 'reg', dependency: null),
|
() => UniversalServiceImpl(value: 'reg', dependency: null),
|
||||||
@@ -113,6 +99,8 @@ class YxScopeAdapter extends DIAdapter<UniversalYxScopeContainer> {
|
|||||||
case UniversalScenario.override:
|
case UniversalScenario.override:
|
||||||
// handled at benchmark level
|
// handled at benchmark level
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (scenario == UniversalScenario.chain ||
|
if (scenario == UniversalScenario.chain ||
|
||||||
scenario == UniversalScenario.override) {
|
scenario == UniversalScenario.override) {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
/// Enum to represent the DI registration/binding mode.
|
/// Enum to represent the DI registration/binding mode.
|
||||||
enum UniversalBindingMode {
|
enum UniversalBindingMode {
|
||||||
/// Singleton/provider binding.
|
/// Eager singleton — instance created at registration time.
|
||||||
singletonStrategy,
|
singletonStrategy,
|
||||||
|
|
||||||
/// Factory-based binding.
|
/// Lazy singleton — instance created on first resolve, then cached.
|
||||||
|
lazySingletonStrategy,
|
||||||
|
|
||||||
|
/// Factory-based binding — new instance every time.
|
||||||
factoryStrategy,
|
factoryStrategy,
|
||||||
|
|
||||||
/// Async-based binding.
|
/// Async-based binding.
|
||||||
|
|||||||
Reference in New Issue
Block a user