From 3a75bd5b284d9abcfbe5ff678e55cd15d02e907a Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Wed, 6 Aug 2025 17:01:55 +0300 Subject: [PATCH] feat(benchmark): add UniversalScenario enum and extend UniversalChainModule to support chain, register, named, override, async scenarios --- benchmark_cherrypick/bin/main.dart | 102 ++++++------------ .../benchmarks/universal_chain_benchmark.dart | 40 +++++++ .../lib/scenarios/universal_chain_module.dart | 60 +++++++++++ .../lib/scenarios/universal_service.dart | 10 ++ 4 files changed, 142 insertions(+), 70 deletions(-) create mode 100644 benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart create mode 100644 benchmark_cherrypick/lib/scenarios/universal_chain_module.dart create mode 100644 benchmark_cherrypick/lib/scenarios/universal_service.dart diff --git a/benchmark_cherrypick/bin/main.dart b/benchmark_cherrypick/bin/main.dart index 2112cf4..2d045d9 100644 --- a/benchmark_cherrypick/bin/main.dart +++ b/benchmark_cherrypick/bin/main.dart @@ -1,58 +1,52 @@ -import 'package:benchmark_cherrypick/benchmarks/register_and_resolve_benchmark.dart'; -import 'package:benchmark_cherrypick/benchmarks/chain_singleton_benchmark.dart'; -import 'package:benchmark_cherrypick/benchmarks/chain_factory_benchmark.dart'; -import 'package:benchmark_cherrypick/benchmarks/named_resolve_benchmark.dart'; -import 'package:benchmark_cherrypick/benchmarks/scope_override_benchmark.dart'; -import 'package:benchmark_cherrypick/benchmarks/async_chain_benchmark.dart'; +import 'package:benchmark_cherrypick/benchmarks/universal_chain_benchmark.dart'; import 'package:benchmark_cherrypick/di_adapters/cherrypick_adapter.dart'; +import 'package:benchmark_cherrypick/scenarios/universal_chain_module.dart'; import 'package:args/args.dart'; import 'dart:io'; import 'dart:math'; Future main(List args) async { final parser = ArgParser() - ..addOption('benchmark', abbr: 'b', help: 'Benchmark name (register, chain_singleton, chain_factory, named, override, async_chain, all)', defaultsTo: 'all') - ..addOption('chainCount', abbr: 'c', help: 'Comma-separated chainCounts (используется в chain_singleton/factory)', defaultsTo: '100') - ..addOption('nestingDepth', abbr: 'd', help: 'Comma-separated depths (используется в chain_singleton/factory)', defaultsTo: '100') - ..addOption('repeat', abbr: 'r', help: 'Repeats for each run (statistical run, >=2)', defaultsTo: '5') - ..addOption('warmup', abbr: 'w', help: 'Warmup runs before timing', defaultsTo: '2') + ..addOption('chainCount', abbr: 'c', help: 'Comma-separated chainCounts', defaultsTo: '10') + ..addOption('nestingDepth', abbr: 'd', help: 'Comma-separated depths', defaultsTo: '5') + ..addOption('mode', abbr: 'm', help: 'Mode (singletonStrategy,factoryStrategy,asyncStrategy)', defaultsTo: 'singletonStrategy') + ..addOption('repeat', abbr: 'r', help: 'Repeats for each run (>=2)', defaultsTo: '2') + ..addOption('warmup', abbr: 'w', help: 'Warmup runs', defaultsTo: '1') ..addOption('format', abbr: 'f', help: 'Output format (pretty, csv, json)', defaultsTo: 'pretty') ..addFlag('help', abbr: 'h', negatable: false, help: 'Show help'); final result = parser.parse(args); if (result['help'] == true) { - print('Dart DI benchmarks'); + print('UniversalChainBenchmark'); print(parser.usage); - print('\nExamples:\n' - ' dart run bin/main.dart --benchmark=chain_singleton --chainCount=10,100 --nestingDepth=5,10 --format=csv\n' - ' dart run bin/main.dart --benchmark=named\n' - 'Extra: --repeat=7 --warmup=3\n'); return; } - final di = CherrypickDIAdapter(); - - final benchmark = result['benchmark'] as String; - final format = result['format'] as String; - final chainCounts = _parseIntList(result['chainCount'] as String); final nestDepths = _parseIntList(result['nestingDepth'] as String); - final repeats = int.tryParse(result['repeat'] as String? ?? "") ?? 5; - final warmups = int.tryParse(result['warmup'] as String? ?? "") ?? 2; + final modeName = result['mode'] as String; + final mode = UniversalBindingMode.values.firstWhere( + (m) => m.toString().split('.').last == modeName, + orElse: () => UniversalBindingMode.singletonStrategy, + ); + final repeats = int.tryParse(result['repeat'] as String? ?? "") ?? 2; + final warmups = int.tryParse(result['warmup'] as String? ?? "") ?? 1; + final format = result['format'] as String; + final di = CherrypickDIAdapter(); final results = >[]; + void addResult( String name, - int? chainCount, - int? nestingDepth, + int chainCount, + int nestingDepth, List timings, int? memoryDiffKb, int? deltaPeakKb, int? peakRssKb, ) { timings.sort(); - var mean = timings.reduce((a, b) => a + b) / timings.length; var median = timings[timings.length ~/ 2]; var minVal = timings.first; @@ -78,8 +72,8 @@ Future main(List args) async { Future runAndCollect( String name, Future Function() fn, { - int? chainCount, - int? nestingDepth, + required int chainCount, + required int nestingDepth, }) async { for (int i = 0; i < warmups; i++) { await fn(); @@ -98,44 +92,18 @@ Future main(List args) async { addResult(name, chainCount, nestingDepth, timings, memDiffKB, deltaPeakKb, (peakRss/1024).round()); } - if (benchmark == 'all' || benchmark == 'register') { - await runAndCollect('RegisterAndResolve', () async { - return _captureReport(RegisterAndResolveBenchmark(di).report); - }); - } - if (benchmark == 'all' || benchmark == 'chain_singleton') { - for (final c in chainCounts) { - for (final d in nestDepths) { - await runAndCollect('ChainSingleton', () async { - return _captureReport(() => ChainSingletonBenchmark(di,chainCount: c, nestingDepth: d).report()); - }, chainCount: c, nestingDepth: d); - } + for (final c in chainCounts) { + for (final d in nestDepths) { + await runAndCollect('UniversalChain_$mode', () async { + return _captureReport(() => UniversalChainBenchmark( + di, + chainCount: c, + nestingDepth: d, + mode: mode, + ).report()); + }, chainCount: c, nestingDepth: d); } } - if (benchmark == 'all' || benchmark == 'chain_factory') { - for (final c in chainCounts) { - for (final d in nestDepths) { - await runAndCollect('ChainFactory', () async { - return _captureReport(() => ChainFactoryBenchmark(di, chainCount: c, nestingDepth: d).report()); - }, chainCount: c, nestingDepth: d); - } - } - } - if (benchmark == 'all' || benchmark == 'named') { - await runAndCollect('NamedResolve', () async { - return _captureReport(NamedResolveBenchmark(di).report); - }); - } - if (benchmark == 'all' || benchmark == 'override') { - await runAndCollect('ScopeOverride', () async { - return _captureReport(ScopeOverrideBenchmark(di).report); - }); - } - if (benchmark == 'all' || benchmark == 'async_chain') { - await runAndCollect('AsyncChain', () async { - return _captureReportAsync(AsyncChainBenchmark(di).report); - }); - } if (format == 'json') { print(_toJson(results)); @@ -155,12 +123,6 @@ Future _captureReport(void Function() fn) async { sw.stop(); return sw.elapsedMicroseconds; } -Future _captureReportAsync(Future Function() fn) async { - final sw = Stopwatch()..start(); - await fn(); - sw.stop(); - return sw.elapsedMicroseconds; -} String _toPretty(List> rows) { final keys = [ diff --git a/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart b/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart new file mode 100644 index 0000000..da166cf --- /dev/null +++ b/benchmark_cherrypick/lib/benchmarks/universal_chain_benchmark.dart @@ -0,0 +1,40 @@ +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'; + +class UniversalChainBenchmark extends BenchmarkBase { + final DIAdapter di; + final int chainCount; + final int nestingDepth; + final UniversalBindingMode mode; + + UniversalChainBenchmark( + this.di, { + this.chainCount = 1, + this.nestingDepth = 3, + this.mode = UniversalBindingMode.singletonStrategy, + }) : super( + 'UniversalChain: $mode. C/D = $chainCount/$nestingDepth', + ); + + @override + void setup() { + di.setupModules([ + UniversalChainModule( + chainCount: chainCount, + nestingDepth: nestingDepth, + bindingMode: mode, + ), + ]); + } + + @override + void teardown() => di.teardown(); + + @override + void run() { + final serviceName = '${chainCount}_$nestingDepth'; + di.resolve(named: serviceName); + } +} diff --git a/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart b/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart new file mode 100644 index 0000000..3c9d6b1 --- /dev/null +++ b/benchmark_cherrypick/lib/scenarios/universal_chain_module.dart @@ -0,0 +1,60 @@ +import 'package:cherrypick/cherrypick.dart'; +import 'universal_service.dart'; + +enum UniversalBindingMode { + singletonStrategy, + factoryStrategy, + asyncStrategy, +} + +class UniversalChainModule extends Module { + final int chainCount; + final int nestingDepth; + final UniversalBindingMode bindingMode; + + UniversalChainModule({ + required this.chainCount, + required this.nestingDepth, + this.bindingMode = UniversalBindingMode.singletonStrategy, + }); + + @override + void builder(Scope currentScope) { + for (var chainIndex = 0; chainIndex < chainCount; chainIndex++) { + for (var levelIndex = 0; levelIndex < nestingDepth; levelIndex++) { + final chain = chainIndex + 1; + final level = levelIndex + 1; + final prevDepName = '${chain}_${level - 1}'; + final depName = '${chain}_$level'; + switch (bindingMode) { + case UniversalBindingMode.singletonStrategy: + bind() + .toProvide(() => UniversalServiceImpl( + value: depName, + dependency: currentScope.tryResolve(named: prevDepName), + )) + .withName(depName) + .singleton(); + break; + case UniversalBindingMode.factoryStrategy: + bind() + .toProvide(() => UniversalServiceImpl( + value: depName, + dependency: currentScope.tryResolve(named: prevDepName), + )) + .withName(depName); + break; + case UniversalBindingMode.asyncStrategy: + bind() + .toProvideAsync(() async => UniversalServiceImpl( + value: depName, + dependency: await currentScope.resolveAsync(named: prevDepName), + )) + .withName(depName) + .singleton(); + break; + } + } + } + } +} diff --git a/benchmark_cherrypick/lib/scenarios/universal_service.dart b/benchmark_cherrypick/lib/scenarios/universal_service.dart new file mode 100644 index 0000000..f6ff736 --- /dev/null +++ b/benchmark_cherrypick/lib/scenarios/universal_service.dart @@ -0,0 +1,10 @@ + +abstract class UniversalService { + final String value; + final UniversalService? dependency; + UniversalService({required this.value, this.dependency}); +} + +class UniversalServiceImpl extends UniversalService { + UniversalServiceImpl({required super.value, super.dependency}); +} \ No newline at end of file