Merge pull request #17 from pese-git/impr/complex_benchmark_impr

impr: BENCHMARK - complex benchmark improvements.
This commit is contained in:
Sergey Penkovsky
2025-08-07 15:01:27 +03:00
committed by GitHub
35 changed files with 1952 additions and 336 deletions

View File

@@ -1,42 +0,0 @@
# benchmark_cherrypick
Benchmarks for performance and features of the cherrypick (core) DI container.
All scenarios use the public API capabilities of cherrypick (scope, module, binding, scoping, and async support).
## Scenarios
- **RegisterAndResolve**: basic registration and resolution of a dependency.
- **ChainSingleton (A->B->C, singleton)**: dependency chain, all as singletons.
- **ChainFactory (A->B->C, factory)**: dependency chain with factory bindings (new instance on each request).
- **NamedResolve (by name)**: resolving a named dependency among multiple implementations.
- **AsyncChain (A->B->C, async)**: asynchronous dependency chain.
- **ScopeOverride (child overrides parent)**: overriding a dependency in a child scope over a parent.
## Benchmark results
| Scenario | RunTime (μs) |
|----------------------------------------------------|---------------|
| RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3611 |
## How to run
1. Get dependencies:
```shell
dart pub get
```
2. Run the benchmarks:
```shell
dart run bin/main.dart
```
A text report with all metrics will be displayed in the console.
---
To add your custom scenario — just create a new Dart file and declare a class extending BenchmarkBase or AsyncBenchmarkBase, then add its invocation to main.dart.

View File

@@ -1,42 +0,0 @@
# benchmark_cherrypick
Бенчмарки производительности и функциональности DI-контейнера cherrypick (core).
Все сценарии используют реальные возможности public API cherrypick (scope, module, binding, scoping и асинхронность).
## Сценарии
- **RegisterAndResolve**: базовая операция регистрации и разрешения зависимости.
- **ChainSingleton (A->B->C, singleton)**: цепочка зависимостей, все singletons.
- **ChainFactory (A->B->C, factory)**: цепочка зависимостей с factory биндингами, новые объекты на каждый запрос.
- **NamedResolve (by name)**: разрешение именованной зависимости среди нескольких реализаций.
- **AsyncChain (A->B->C, async)**: асинхронная цепочка зависимостей.
- **ScopeOverride (child overrides parent)**: переопределение зависимости в дочернем scope над родительским.
## Результаты исследования
| Сценарий | RunTime (мкс) |
|----------------------------------------------------|--------------|
| RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3611 |
## Как запускать
1. Получить зависимости:
```shell
dart pub get
```
2. Запустить бенчмарк:
```shell
dart run bin/main.dart
```
Будет показан текстовый отчёт по всем метрикам.
---
Если хотите добавить свой сценарий — создайте отдельный Dart-файл и объявите новый BenchmarkBase/AsyncBenchmarkBase, не забудьте вставить его вызов в main.

View File

@@ -1,17 +0,0 @@
import 'package:benchmark_cherrypick/cherrypick_benchmark.dart';
import 'package:benchmark_cherrypick/complex_bindings_benchmark.dart';
import 'package:benchmark_cherrypick/async_chain_benchmark.dart';
import 'package:benchmark_cherrypick/scope_override_benchmark.dart';
void main(List<String> args) async {
// Синхронные бенчмарки
RegisterAndResolveBenchmark().report();
ChainSingletonBenchmark().report();
ChainFactoryBenchmark().report();
NamedResolveBenchmark().report();
// Асинхронный бенчмарк
await AsyncChainBenchmark().report();
ScopeOverrideBenchmark().report();
}

View File

@@ -1,43 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class AsyncA {}
class AsyncB {
final AsyncA a;
AsyncB(this.a);
}
class AsyncC {
final AsyncB b;
AsyncC(this.b);
}
class AsyncChainModule extends Module {
@override
void builder(Scope currentScope) {
bind<AsyncA>().toProvideAsync(() async => AsyncA()).singleton();
bind<AsyncB>().toProvideAsync(() async => AsyncB(await currentScope.resolveAsync<AsyncA>())).singleton();
bind<AsyncC>().toProvideAsync(() async => AsyncC(await currentScope.resolveAsync<AsyncB>())).singleton();
}
}
class AsyncChainBenchmark extends AsyncBenchmarkBase {
AsyncChainBenchmark() : super('AsyncChain (A->B->C, async)');
late Scope scope;
@override
Future<void> setup() async {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([AsyncChainModule()]);
}
@override
Future<void> teardown() async {
CherryPick.closeRootScope();
}
@override
Future<void> run() async {
await scope.resolveAsync<AsyncC>();
}
}

View File

@@ -1,40 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<FooService>().toProvide(() => FooService());
}
}
// Dummy service for DI
class FooService {}
class RegisterAndResolveBenchmark extends BenchmarkBase {
RegisterAndResolveBenchmark() : super('RegisterAndResolve');
late final Scope scope;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([AppModule()]);
}
@override
void run() {
scope.resolve<FooService>();
}
@override
void teardown() => CherryPick.closeRootScope();
}
void main() {
RegisterAndResolveBenchmark().report();
}

View File

@@ -1,95 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
// === DI graph: A -> B -> C (singleton) ===
class ServiceA {}
class ServiceB {
final ServiceA a;
ServiceB(this.a);
}
class ServiceC {
final ServiceB b;
ServiceC(this.b);
}
class ChainSingletonModule extends Module {
@override
void builder(Scope currentScope) {
bind<ServiceA>().toProvide(() => ServiceA()).singleton();
bind<ServiceB>().toProvide((() => ServiceB(currentScope.resolve<ServiceA>()))).singleton();
bind<ServiceC>().toProvide((() => ServiceC(currentScope.resolve<ServiceB>()))).singleton();
}
}
class ChainSingletonBenchmark extends BenchmarkBase {
ChainSingletonBenchmark() : super('ChainSingleton (A->B->C, singleton)');
late Scope scope;
@override
void setup() {
scope = CherryPick.openRootScope();
scope.installModules([ChainSingletonModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
scope.resolve<ServiceC>();
}
}
// === DI graph: A -> B -> C (factory/no singleton) ===
class ChainFactoryModule extends Module {
@override
void builder(Scope currentScope) {
bind<ServiceA>().toProvide(() => ServiceA());
bind<ServiceB>().toProvide((() => ServiceB(currentScope.resolve<ServiceA>())));
bind<ServiceC>().toProvide((() => ServiceC(currentScope.resolve<ServiceB>())));
}
}
class ChainFactoryBenchmark extends BenchmarkBase {
ChainFactoryBenchmark() : super('ChainFactory (A->B->C, factory)');
late Scope scope;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([ChainFactoryModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
scope.resolve<ServiceC>();
}
}
// === Named bindings: Multiple implementations ===
class Impl1 {}
class Impl2 {}
class NamedModule extends Module {
@override
void builder(Scope currentScope) {
bind<Object>().toProvide(() => Impl1()).withName('impl1');
bind<Object>().toProvide(() => Impl2()).withName('impl2');
}
}
class NamedResolveBenchmark extends BenchmarkBase {
NamedResolveBenchmark() : super('NamedResolve (by name)');
late Scope scope;
@override
void setup() {
scope = CherryPick.openRootScope();
scope.installModules([NamedModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
// Switch name for comparison
scope.resolve<Object>(named: 'impl2');
}
}

View File

@@ -1,46 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class Shared {}
class ParentImpl extends Shared {}
class ChildImpl extends Shared {}
class ParentModule extends Module {
@override
void builder(Scope currentScope) {
bind<Shared>().toProvide(() => ParentImpl()).singleton();
}
}
class ChildOverrideModule extends Module {
@override
void builder(Scope currentScope) {
bind<Shared>().toProvide(() => ChildImpl()).singleton();
}
}
class ScopeOverrideBenchmark extends BenchmarkBase {
ScopeOverrideBenchmark() : super('ScopeOverride (child overrides parent)');
late Scope parent;
late Scope child;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
parent = CherryPick.openRootScope();
parent.installModules([ParentModule()]);
child = parent.openSubScope('child');
child.installModules([ChildOverrideModule()]);
}
@override
void teardown() {
CherryPick.closeRootScope();
}
@override
void run() {
// Должен возвращать ChildImpl, а не ParentImpl
final resolved = child.resolve<Shared>();
assert(resolved is ChildImpl);
}
}

275
benchmark_di/README.md Normal file
View File

@@ -0,0 +1,275 @@
# benchmark_di
_Benchmark suite for cherrypick DI container, get_it, and other DI solutions._
## Overview
benchmark_di is a flexible benchmarking suite to compare DI containers (like cherrypick and get_it) on synthetic, deep, and real-world dependency scenarios chains, factories, async, named, override, etc.
**Features:**
- Universal registration layer and modular scenario setup (works with any DI)
- Built-in support for [cherrypick](https://github.com/) and [get_it](https://pub.dev/packages/get_it)
- Clean CLI for matrix runs and output formats (Markdown, CSV, JSON, pretty)
- Reports metrics: timings, memory (RSS, peak), statistical spreads, and more
- Extendable via your own DIAdapter or benchmark scenarios
---
## Benchmark Scenarios
- **registerSingleton**: Simple singleton registration/resolution
- **chainSingleton**: Resolution of long singleton chains (A→B→C...)
- **chainFactory**: Chain resolution via factories (new instances each time)
- **asyncChain**: Async chain (with async providers)
- **named**: Named/qualified resolution (e.g. from multiple implementations)
- **override**: Resolution and override in subScopes/child adapters
---
## Supported DI
- **cherrypick** (default)
- **get_it**
- Easy to add your own DI by creating a DIAdapter
Switch DI with the CLI option: `--di`
---
## How to Run
1. **Install dependencies:**
```shell
dart pub get
```
2. **Run all benchmarks (default: all scenarios, 2 warmup, 2 repeats):**
```shell
dart run bin/main.dart --benchmark=all --format=markdown
```
3. **For get_it:**
```shell
dart run bin/main.dart --di=getit --benchmark=all --format=markdown
```
4. **Show all CLI options:**
```shell
dart run bin/main.dart --help
```
### CLI Parameters
- `--di` — DI implementation: `cherrypick` (default) or `getit`
- `--benchmark, -b` — Scenario: `registerSingleton`, `chainSingleton`, `chainFactory`, `asyncChain`, `named`, `override`, `all`
- `--chainCount, -c` — Number of parallel chains (e.g. `10,100`)
- `--nestingDepth, -d` — Chain depth (e.g. `5,10`)
- `--repeat, -r` — Measurement repeats (default: 2)
- `--warmup, -w` — Warmup runs (default: 1)
- `--format, -f` — Output: `pretty`, `csv`, `json`, `markdown`
- `--help, -h` — Usage
### Run Examples
- **All benchmarks for cherrypick:**
```shell
dart run bin/main.dart --di=cherrypick --benchmark=all --format=markdown
```
- **For get_it (all scenarios):**
```shell
dart run bin/main.dart --di=getit --benchmark=all --format=markdown
```
- **Specify chains/depth matrix:**
```shell
dart run bin/main.dart --benchmark=chainSingleton --chainCount=10,100 --nestingDepth=5,10 --repeat=3 --format=csv
```
---
## Universal DI registration: Adapter-centric approach
Starting from vX.Y.Z, all DI registration scenarios and logic are encapsulated in the adapter itself via the `universalRegistration` method.
### How to use (in Dart code):
```dart
final di = CherrypickDIAdapter(); // or GetItAdapter(), RiverpodAdapter(), etc
di.setupDependencies(
di.universalRegistration(
scenario: UniversalScenario.chain,
chainCount: 10,
nestingDepth: 5,
bindingMode: UniversalBindingMode.singletonStrategy,
),
);
```
- There is **no more need to use any global function or switch**: each adapter provides its own type-safe implementation.
### How to add a new scenario or DI:
- Implement `universalRegistration<S extends Enum>(...)` in your adapter
- Use your own Enum if you want adapter-specific scenarios!
- Benchmarks and CLI become automatically extensible for custom DI and scenarios.
### CLI usage (runs all universal scenarios for Cherrypick, GetIt, Riverpod):
```
dart run bin/main.dart --di=cherrypick --benchmark=all
dart run bin/main.dart --di=getit --benchmark=all
dart run bin/main.dart --di=riverpod --benchmark=all
```
See the `benchmark_di/lib/di_adapters/` folder for ready-to-use adapters.
---
## Advantages
- **Type-safe:** Zero dynamic/object usage in DI flows.
- **Extensible:** New scenarios are just new Enum values and a method extension.
- **No global registration logic:** All DI-related logic is where it belongs: in the adapter.
=======
## How to Add Your Own DI
1. Implement a class extending `DIAdapter` (`lib/di_adapters/your_adapter.dart`)
2. Implement the `universalRegistration<S extends Enum>(...)` method directly in your adapter for type-safe and scenario-specific registration
3. Register your adapter in CLI (see `cli/benchmark_cli.dart`)
4. No global function needed — all logic is within the adapter!
---
## Universal DI registration: Adapter-centric approach
Starting from vX.Y.Z, all DI registration scenarios and logic are encapsulated in the adapter itself via the `universalRegistration` method.
### How to use (in Dart code):
```dart
final di = CherrypickDIAdapter(); // or GetItAdapter(), RiverpodAdapter(), etc
di.setupDependencies(
di.universalRegistration(
scenario: UniversalScenario.chain,
chainCount: 10,
nestingDepth: 5,
bindingMode: UniversalBindingMode.singletonStrategy,
),
);
```
- There is **no more need to use any global function or switch**: each adapter provides its own type-safe implementation.
### How to add a new scenario or DI:
- Implement `universalRegistration<S extends Enum>(...)` in your adapter
- Use your own Enum if you want adapter-specific scenarios!
- Benchmarks and CLI become automatically extensible for custom DI and scenarios.
### CLI usage (runs all universal scenarios for Cherrypick, GetIt, Riverpod):
```
dart run bin/main.dart --di=cherrypick --benchmark=all
dart run bin/main.dart --di=getit --benchmark=all
dart run bin/main.dart --di=riverpod --benchmark=all
```
See the `benchmark_di/lib/di_adapters/` folder for ready-to-use adapters.
## Advantages
- **Type-safe:** Zero dynamic/object usage in DI flows.
- **Extensible:** New scenarios are just new Enum values and a method extension.
- **No global registration logic:** All DI-related logic is where it belongs: in the adapter.
---
## Architecture
```mermaid
classDiagram
class BenchmarkCliRunner {
+run(args)
}
class UniversalChainBenchmark~TContainer~ {
+setup()
+run()
+teardown()
}
class UniversalChainAsyncBenchmark~TContainer~ {
+setup()
+run()
+teardown()
}
class DIAdapter~TContainer~ {
<<interface>>
+setupDependencies(cb)
+resolve~T~(named)
+resolveAsync~T~(named)
+teardown()
+openSubScope(name)
+waitForAsyncReady()
+universalRegistration<S extends Enum>(...)
}
class CherrypickDIAdapter
class GetItAdapter
class RiverpodAdapter
class UniversalChainModule {
+builder(scope)
+chainCount
+nestingDepth
+bindingMode
+scenario
}
class UniversalService {
<<interface>>
+value
+dependency
}
class UniversalServiceImpl {
+UniversalServiceImpl(value, dependency)
}
class Scope
class UniversalScenario
class UniversalBindingMode
%% Relationships
BenchmarkCliRunner --> UniversalChainBenchmark
BenchmarkCliRunner --> UniversalChainAsyncBenchmark
UniversalChainBenchmark *-- DIAdapter
UniversalChainAsyncBenchmark *-- DIAdapter
DIAdapter <|.. CherrypickDIAdapter
DIAdapter <|.. GetItAdapter
DIAdapter <|.. RiverpodAdapter
CherrypickDIAdapter ..> Scope
GetItAdapter ..> GetIt: "uses GetIt"
RiverpodAdapter ..> Map~String, ProviderBase~: "uses Provider registry"
DIAdapter o--> UniversalChainModule : setupDependencies
UniversalChainModule ..> UniversalScenario
UniversalChainModule ..> UniversalBindingMode
UniversalChainModule o-- UniversalServiceImpl : creates
UniversalService <|.. UniversalServiceImpl
UniversalServiceImpl --> UniversalService : dependency
%%
%% Each concrete adapter implements universalRegistration<S extends Enum>
%% You can add new scenario enums for custom adapters
%% Extensibility is achieved via adapter logic, not global functions
```
---
## Metrics
Always collected:
- **Timings** (microseconds): mean, median, stddev, min, max
- **Memory**: RSS difference, peak RSS
## License
MIT

226
benchmark_di/README.ru.md Normal file
View File

@@ -0,0 +1,226 @@
# benchmark_di
енчмаркинговый набор для cherrypick, get_it и других DI-контейнеров._
## Общее описание
benchmark_di — это современный фреймворк для измерения производительности DI-контейнеров (как cherrypick, так и get_it) на синтетических, сложных и реальных сценариях: цепочки зависимостей, factory, async, именованные биндинги, override и пр.
**Возможности:**
- Универсальный слой регистрации сценариев (работает с любым DI)
- Готовая поддержка [cherrypick](https://github.com/) и [get_it](https://pub.dev/packages/get_it)
- Удобный CLI для запусков по матрице значений параметров и различных форматов вывода (Markdown, CSV, JSON, pretty)
- Сбор и вывод метрик: время, память (RSS, peak), статистика (среднее, медиана, stddev, min/max)
- Легко расширять — создавайте свой DIAdapter и новые сценарии
---
## Сценарии бенчмарков
- **registerSingleton**: Регистрация и резолвинг singleton
- **chainSingleton**: Разрешение длинных singleton-цепочек (A→B→C…)
- **chainFactory**: То же, но с factory (каждый раз — новый объект)
- **asyncChain**: Асинхронная цепочка (async factory/provider)
- **named**: Разрешение по имени (например, из нескольких реализаций)
- **override**: Переопределение зависимостей в subScope
---
## Поддерживаемые DI-контейнеры
- **cherrypick** (по умолчанию)
- **get_it**
- Легко добавить свой DI через DIAdapter
Меняется одной CLI-опцией: `--di`
---
## Как запустить
1. **Установить зависимости:**
```shell
dart pub get
```
2. **Запустить все бенчмарки (по умолчанию: все сценарии, 2 прогрева, 2 замера):**
```shell
dart run bin/main.dart --benchmark=all --format=markdown
```
3. **Для get_it:**
```shell
dart run bin/main.dart --di=getit --benchmark=all --format=markdown
```
4. **Показать все опции CLI:**
```shell
dart run bin/main.dart --help
```
### Параметры CLI
- `--di` — Какой DI использовать: `cherrypick` (по умолчанию) или `getit`
- `--benchmark, -b` — Сценарий: `registerSingleton`, `chainSingleton`, `chainFactory`, `asyncChain`, `named`, `override`, `all`
- `--chainCount, -c` — Сколько параллельных цепочек (например, `10,100`)
- `--nestingDepth, -d` — Глубина цепочки (например, `5,10`)
- `--repeat, -r` — Повторов замера (по умолчанию 2)
- `--warmup, -w` — Прогревочных запусков (по умолчанию 1)
- `--format, -f` — Формат отчёта: `pretty`, `csv`, `json`, `markdown`
- `--help, -h` — Справка
### Примеры запуска
- **Все бенчмарки для cherrypick:**
```shell
dart run bin/main.dart --di=cherrypick --benchmark=all --format=markdown
```
- **Для get_it (все сценарии):**
```shell
dart run bin/main.dart --di=getit --benchmark=all --format=markdown
```
- **Запуск по матрице параметров:**
```shell
dart run bin/main.dart --benchmark=chainSingleton --chainCount=10,100 --nestingDepth=5,10 --repeat=3 --format=csv
```
---
## Универсальная регистрация зависимостей: теперь через adapter
В версии X.Y.Z вся логика сценариев регистрации DI-инфраструктуры локализована в adapter через метод `universalRegistration`.
### Использование в Dart:
```dart
final di = CherrypickDIAdapter(); // или GetItAdapter(), RiverpodAdapter() и т.д.
di.setupDependencies(
di.universalRegistration(
scenario: UniversalScenario.chain,
chainCount: 10,
nestingDepth: 5,
bindingMode: UniversalBindingMode.singletonStrategy,
),
);
```
- **Теперь нет необходимости вызывать глобальные функции или switch-case по типу DI!** Каждый adapter сам предоставляет типобезопасную функцию регистрации.
### Как добавить новый сценарий или DI:
- Реализуйте метод `universalRegistration<S extends Enum>(...)` в своём adapter.
- Можно использовать как UniversalScenario, так и собственные enum-сценарии!
- Бенчмарки CLI автоматически расширяются под ваш DI и ваши сценарии, если реализован метод-расширение.
### Запуск CLI (все сценарии DI Cherrypick, GetIt, Riverpod):
```
dart run bin/main.dart --di=cherrypick --benchmark=all
dart run bin/main.dart --di=getit --benchmark=all
dart run bin/main.dart --di=riverpod --benchmark=all
```
Смотрите примеры готовых adapters в `benchmark_di/lib/di_adapters/`.
## Преимущества
- **Type-safe:** Исключено использование dynamic/object в стороне DI.
- **Масштабируемость:** Новый сценарий — просто enum + метод в adapter.
- **Вся логика регистрации теперь только в adapter:** Добавление или изменение не затрагивает глобальные функции.
---
## Архитектура
```mermaid
classDiagram
class BenchmarkCliRunner {
+run(args)
}
class UniversalChainBenchmark~TContainer~ {
+setup()
+run()
+teardown()
}
class UniversalChainAsyncBenchmark~TContainer~ {
+setup()
+run()
+teardown()
}
class DIAdapter~TContainer~ {
<<interface>>
+setupDependencies(cb)
+resolve~T~(named)
+resolveAsync~T~(named)
+teardown()
+openSubScope(name)
+waitForAsyncReady()
+universalRegistration<S extends Enum>(...)
}
class CherrypickDIAdapter
class GetItAdapter
class RiverpodAdapter
class UniversalChainModule {
+builder(scope)
+chainCount
+nestingDepth
+bindingMode
+scenario
}
class UniversalService {
<<interface>>
+value
+dependency
}
class UniversalServiceImpl {
+UniversalServiceImpl(value, dependency)
}
class Scope
class UniversalScenario
class UniversalBindingMode
%% Relationships
BenchmarkCliRunner --> UniversalChainBenchmark
BenchmarkCliRunner --> UniversalChainAsyncBenchmark
UniversalChainBenchmark *-- DIAdapter
UniversalChainAsyncBenchmark *-- DIAdapter
DIAdapter <|.. CherrypickDIAdapter
DIAdapter <|.. GetItAdapter
DIAdapter <|.. RiverpodAdapter
CherrypickDIAdapter ..> Scope
GetItAdapter ..> GetIt: "uses GetIt"
RiverpodAdapter ..> Map~String, ProviderBase~: "uses Provider registry"
DIAdapter o--> UniversalChainModule : setupDependencies
UniversalChainModule ..> UniversalScenario
UniversalChainModule ..> UniversalBindingMode
UniversalChainModule o-- UniversalServiceImpl : creates
UniversalService <|.. UniversalServiceImpl
UniversalServiceImpl --> UniversalService : dependency
%%
%% Each concrete adapter implements universalRegistration<S extends Enum>
%% You can add new scenario enums for custom adapters
%% Extensibility is achieved via adapter logic, not global functions
```
---
## Метрики
Всегда собираются:
- **Время** (мкс): среднее, медиана, stddev, min, max
- **Память**: прирост RSS, пиковое значение RSS
## Лицензия
MIT

79
benchmark_di/REPORT.md Normal file
View File

@@ -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.

79
benchmark_di/REPORT.ru.md Normal file
View File

@@ -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.

View File

@@ -15,6 +15,7 @@ include: package:lints/recommended.yaml
analyzer:
errors:
deprecated_member_use: ignore
depend_on_referenced_packages: ignore
# Uncomment the following section to specify additional rules.

View File

@@ -0,0 +1,5 @@
import 'package:benchmark_di/cli/benchmark_cli.dart';
Future<void> main(List<String> args) async {
await BenchmarkCliRunner().run(args);
}

View File

@@ -0,0 +1,41 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:benchmark_di/di_adapters/di_adapter.dart';
import 'package:benchmark_di/scenarios/universal_service.dart';
class UniversalChainAsyncBenchmark<TContainer> extends AsyncBenchmarkBase {
final DIAdapter<TContainer> di;
final int chainCount;
final int nestingDepth;
final UniversalBindingMode mode;
UniversalChainAsyncBenchmark(
this.di, {
this.chainCount = 1,
this.nestingDepth = 3,
this.mode = UniversalBindingMode.asyncStrategy,
}) : super('UniversalAsync: asyncChain/$mode CD=$chainCount/$nestingDepth');
@override
Future<void> setup() async {
di.setupDependencies(di.universalRegistration(
chainCount: chainCount,
nestingDepth: nestingDepth,
bindingMode: mode,
scenario: UniversalScenario.asyncChain,
));
await di.waitForAsyncReady();
}
@override
Future<void> teardown() async {
di.teardown();
}
@override
Future<void> run() async {
final serviceName = '${chainCount}_$nestingDepth';
await di.resolveAsync<UniversalService>(named: serviceName);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:benchmark_di/di_adapters/di_adapter.dart';
import 'package:benchmark_di/scenarios/universal_service.dart';
class UniversalChainBenchmark<TContainer> extends BenchmarkBase {
final DIAdapter<TContainer> _di;
final int chainCount;
final int nestingDepth;
final UniversalBindingMode mode;
final UniversalScenario scenario;
DIAdapter<TContainer>? _childDi;
UniversalChainBenchmark(
this._di, {
this.chainCount = 1,
this.nestingDepth = 3,
this.mode = UniversalBindingMode.singletonStrategy,
this.scenario = UniversalScenario.chain,
}) : super('Universal: $scenario/$mode CD=$chainCount/$nestingDepth');
@override
void setup() {
switch (scenario) {
case UniversalScenario.override:
_di.setupDependencies(_di.universalRegistration(
chainCount: chainCount,
nestingDepth: nestingDepth,
bindingMode: UniversalBindingMode.singletonStrategy,
scenario: UniversalScenario.chain,
));
_childDi = _di.openSubScope('child');
_childDi!.setupDependencies(_childDi!.universalRegistration(
chainCount: chainCount,
nestingDepth: nestingDepth,
bindingMode: UniversalBindingMode.singletonStrategy,
scenario: UniversalScenario.chain,
));
break;
default:
_di.setupDependencies(_di.universalRegistration(
chainCount: chainCount,
nestingDepth: nestingDepth,
bindingMode: mode,
scenario: scenario,
));
break;
}
}
@override
void teardown() => _di.teardown();
@override
void run() {
switch (scenario) {
case UniversalScenario.register:
_di.resolve<UniversalService>();
break;
case UniversalScenario.named:
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';
_di.resolve<UniversalService>(named: serviceName);
break;
case UniversalScenario.override:
_childDi!.resolve<UniversalService>();
break;
case UniversalScenario.asyncChain:
throw UnsupportedError('asyncChain supported only in UniversalChainAsyncBenchmark');
}
}
}

View File

@@ -0,0 +1,133 @@
import 'dart:math';
import 'package:benchmark_di/cli/report/markdown_report.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:cherrypick/cherrypick.dart';
import 'package:get_it/get_it.dart';
import 'package:riverpod/riverpod.dart' as rp;
import 'report/pretty_report.dart';
import 'report/csv_report.dart';
import 'report/json_report.dart';
import 'parser.dart';
import 'runner.dart';
import 'package:benchmark_di/benchmarks/universal_chain_benchmark.dart';
import 'package:benchmark_di/benchmarks/universal_chain_async_benchmark.dart';
import 'package:benchmark_di/di_adapters/cherrypick_adapter.dart';
import 'package:benchmark_di/di_adapters/get_it_adapter.dart';
import 'package:benchmark_di/di_adapters/riverpod_adapter.dart';
/// Command-line interface (CLI) runner for benchmarks.
///
/// Parses CLI arguments, orchestrates benchmarks for different
/// scenarios and configurations, collects results, and generates reports
/// in the desired output format.
class BenchmarkCliRunner {
/// Runs benchmarks based on CLI [args], configuring different test scenarios.
Future<void> run(List<String> args) async {
final config = parseBenchmarkCli(args);
final results = <Map<String, dynamic>>[];
for (final bench in config.benchesToRun) {
final scenario = toScenario(bench);
final mode = toMode(bench);
for (final c in config.chainCounts) {
for (final d in config.nestDepths) {
BenchmarkResult benchResult;
if (config.di == 'getit') {
final di = GetItAdapter();
if (scenario == UniversalScenario.asyncChain) {
final benchAsync = UniversalChainAsyncBenchmark<GetIt>(di,
chainCount: c, nestingDepth: d, mode: mode,
);
benchResult = await BenchmarkRunner.runAsync(
benchmark: benchAsync,
warmups: config.warmups,
repeats: config.repeats,
);
} else {
final benchSync = UniversalChainBenchmark<GetIt>(di,
chainCount: c, nestingDepth: d, mode: mode, scenario: scenario,
);
benchResult = await BenchmarkRunner.runSync(
benchmark: benchSync,
warmups: config.warmups,
repeats: config.repeats,
);
}
} else if (config.di == 'riverpod') {
final di = RiverpodAdapter();
if (scenario == UniversalScenario.asyncChain) {
final benchAsync = UniversalChainAsyncBenchmark<Map<String, rp.ProviderBase<Object?>>>(di,
chainCount: c, nestingDepth: d, mode: mode,
);
benchResult = await BenchmarkRunner.runAsync(
benchmark: benchAsync,
warmups: config.warmups,
repeats: config.repeats,
);
} else {
final benchSync = UniversalChainBenchmark<Map<String, rp.ProviderBase<Object?>>>(di,
chainCount: c, nestingDepth: d, mode: mode, scenario: scenario,
);
benchResult = await BenchmarkRunner.runSync(
benchmark: benchSync,
warmups: config.warmups,
repeats: config.repeats,
);
}
} else {
final di = CherrypickDIAdapter();
if (scenario == UniversalScenario.asyncChain) {
final benchAsync = UniversalChainAsyncBenchmark<Scope>(di,
chainCount: c, nestingDepth: d, mode: mode,
);
benchResult = await BenchmarkRunner.runAsync(
benchmark: benchAsync,
warmups: config.warmups,
repeats: config.repeats,
);
} else {
final benchSync = UniversalChainBenchmark<Scope>(di,
chainCount: c, nestingDepth: d, mode: mode, scenario: scenario,
);
benchResult = await BenchmarkRunner.runSync(
benchmark: benchSync,
warmups: config.warmups,
repeats: config.repeats,
);
}
}
final timings = benchResult.timings;
timings.sort();
var mean = timings.reduce((a, b) => a + b) / timings.length;
var median = timings[timings.length ~/ 2];
var minVal = timings.first;
var maxVal = timings.last;
var stddev = timings.isEmpty ? 0 : sqrt(timings.map((x) => pow(x - mean, 2)).reduce((a, b) => a + b) / timings.length);
results.add({
'benchmark': 'Universal_$bench',
'chainCount': c,
'nestingDepth': d,
'mean_us': mean.toStringAsFixed(2),
'median_us': median.toStringAsFixed(2),
'stddev_us': stddev.toStringAsFixed(2),
'min_us': minVal.toStringAsFixed(2),
'max_us': maxVal.toStringAsFixed(2),
'trials': timings.length,
'timings_us': timings.map((t) => t.toStringAsFixed(2)).toList(),
'memory_diff_kb': benchResult.memoryDiffKb,
'delta_peak_kb': benchResult.deltaPeakKb,
'peak_rss_kb': benchResult.peakRssKb,
});
}
}
}
final reportGenerators = {
'pretty': PrettyReport(),
'csv': CsvReport(),
'json': JsonReport(),
'markdown': MarkdownReport(),
};
print(reportGenerators[config.format]?.render(results) ?? PrettyReport().render(results));
}
}

View File

@@ -0,0 +1,130 @@
import 'dart:io';
import 'package:args/args.dart';
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
/// Enum describing all supported Universal DI benchmark types.
enum UniversalBenchmark {
/// Simple singleton registration benchmark
registerSingleton,
/// Chain of singleton dependencies
chainSingleton,
/// Chain using factories
chainFactory,
/// Async chain resolution
chainAsync,
/// Named registration benchmark
named,
/// Override/child-scope benchmark
override,
}
/// Maps [UniversalBenchmark] to the scenario enum for DI chains.
UniversalScenario toScenario(UniversalBenchmark b) {
switch (b) {
case UniversalBenchmark.registerSingleton:
return UniversalScenario.register;
case UniversalBenchmark.chainSingleton:
return UniversalScenario.chain;
case UniversalBenchmark.chainFactory:
return UniversalScenario.chain;
case UniversalBenchmark.chainAsync:
return UniversalScenario.asyncChain;
case UniversalBenchmark.named:
return UniversalScenario.named;
case UniversalBenchmark.override:
return UniversalScenario.override;
}
}
/// Maps benchmark to registration mode (singleton/factory/async).
UniversalBindingMode toMode(UniversalBenchmark b) {
switch (b) {
case UniversalBenchmark.registerSingleton:
return UniversalBindingMode.singletonStrategy;
case UniversalBenchmark.chainSingleton:
return UniversalBindingMode.singletonStrategy;
case UniversalBenchmark.chainFactory:
return UniversalBindingMode.factoryStrategy;
case UniversalBenchmark.chainAsync:
return UniversalBindingMode.asyncStrategy;
case UniversalBenchmark.named:
return UniversalBindingMode.singletonStrategy;
case UniversalBenchmark.override:
return UniversalBindingMode.singletonStrategy;
}
}
/// Utility to parse a string into its corresponding enum value [T].
T parseEnum<T>(String value, List<T> values, T defaultValue) {
return values.firstWhere(
(v) => v.toString().split('.').last.toLowerCase() == value.toLowerCase(),
orElse: () => defaultValue,
);
}
/// Parses comma-separated integer list from [s].
List<int> parseIntList(String s) =>
s.split(',').map((e) => int.tryParse(e.trim()) ?? 0).where((x) => x > 0).toList();
/// CLI config describing what and how to benchmark.
class BenchmarkCliConfig {
/// Benchmarks enabled to run (scenarios).
final List<UniversalBenchmark> benchesToRun;
/// List of chain counts (parallel, per test).
final List<int> chainCounts;
/// List of nesting depths (max chain length, per test).
final List<int> nestDepths;
/// How many times to repeat each trial.
final int repeats;
/// How many times to warm-up before measuring.
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,
required this.nestDepths,
required this.repeats,
required this.warmups,
required this.format,
required this.di,
});
}
/// Parses CLI arguments [args] into a [BenchmarkCliConfig].
/// Supports --benchmark, --chainCount, --nestingDepth, etc.
BenchmarkCliConfig parseBenchmarkCli(List<String> args) {
final parser = ArgParser()
..addOption('benchmark', abbr: 'b', defaultsTo: 'chainSingleton')
..addOption('chainCount', abbr: 'c', defaultsTo: '10')
..addOption('nestingDepth', abbr: 'd', defaultsTo: '5')
..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, getit or riverpod')
..addFlag('help', abbr: 'h', negatable: false, help: 'Show help');
final result = parser.parse(args);
if (result['help'] == true) {
print(parser.usage);
exit(0);
}
final benchName = result['benchmark'] as String;
final isAll = benchName == 'all';
final allBenches = UniversalBenchmark.values;
final benchesToRun = isAll
? allBenches
: [parseEnum(benchName, allBenches, UniversalBenchmark.chainSingleton)];
return BenchmarkCliConfig(
benchesToRun: benchesToRun,
chainCounts: parseIntList(result['chainCount'] as String),
nestDepths: parseIntList(result['nestingDepth'] as String),
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',
);
}

View File

@@ -0,0 +1,24 @@
import 'report_generator.dart';
/// Generates a CSV-formatted report for benchmark results.
class CsvReport extends ReportGenerator {
/// List of all keys/columns to include in the CSV output.
@override
final List<String> keys = [
'benchmark','chainCount','nestingDepth','mean_us','median_us','stddev_us',
'min_us','max_us','trials','timings_us','memory_diff_kb','delta_peak_kb','peak_rss_kb'
];
/// Renders rows as a CSV table string.
@override
String render(List<Map<String, dynamic>> rows) {
final header = keys.join(',');
final lines = rows.map((r) =>
keys.map((k) {
final v = r[k];
if (v is List) return '"${v.join(';')}"';
return (v ?? '').toString();
}).join(',')
).toList();
return ([header] + lines).join('\n');
}
}

View File

@@ -0,0 +1,13 @@
import 'report_generator.dart';
/// Generates a JSON-formatted report for benchmark results.
class JsonReport extends ReportGenerator {
/// No specific keys; outputs all fields in raw map.
@override
List<String> get keys => [];
/// Renders all result rows as a pretty-printed JSON array.
@override
String render(List<Map<String, dynamic>> rows) {
return '[\n${rows.map((r) => ' $r').join(',\n')}\n]';
}
}

View File

@@ -0,0 +1,78 @@
import 'report_generator.dart';
/// Generates a Markdown-formatted report for benchmark results.
///
/// Displays result rows as a visually clear Markdown table including a legend for all metrics.
class MarkdownReport extends ReportGenerator {
/// List of columns (keys) to show in the Markdown table.
@override
final List<String> keys = [
'benchmark','chainCount','nestingDepth','mean_us','median_us','stddev_us',
'min_us','max_us','trials','memory_diff_kb','delta_peak_kb','peak_rss_kb'
];
/// Friendly display names for each benchmark type.
static const nameMap = {
'Universal_UniversalBenchmark.registerSingleton':'RegisterSingleton',
'Universal_UniversalBenchmark.chainSingleton':'ChainSingleton',
'Universal_UniversalBenchmark.chainFactory':'ChainFactory',
'Universal_UniversalBenchmark.chainAsync':'AsyncChain',
'Universal_UniversalBenchmark.named':'Named',
'Universal_UniversalBenchmark.override':'Override',
};
/// Renders all results as a formatted Markdown table with aligned columns and a legend.
@override
String render(List<Map<String, dynamic>> rows) {
final headers = [
'Benchmark', 'Chain Count', 'Depth', 'Mean (us)', 'Median', 'Stddev', 'Min', 'Max', 'N', 'ΔRSS(KB)', 'ΔPeak(KB)', 'PeakRSS(KB)'
];
final dataRows = rows.map((r) {
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
return [
readableName,
r['chainCount'],
r['nestingDepth'],
r['mean_us'],
r['median_us'],
r['stddev_us'],
r['min_us'],
r['max_us'],
r['trials'],
r['memory_diff_kb'],
r['delta_peak_kb'],
r['peak_rss_kb'],
].map((cell) => cell.toString()).toList();
}).toList();
// Calculate column width for pretty alignment
final all = [headers] + dataRows;
final widths = List.generate(headers.length, (i) {
return all.map((row) => row[i].length).reduce((a, b) => a > b ? a : b);
});
String rowToLine(List<String> row, {String sep = ' | '}) =>
'| ${List.generate(row.length, (i) => row[i].padRight(widths[i])).join(sep)} |';
final headerLine = rowToLine(headers);
final divider = '| ${widths.map((w) => '-' * w).join(' | ')} |';
final lines = dataRows.map(rowToLine).toList();
final legend = '''
> **Legend:**
> `Benchmark` Test name
> `Chain Count` Number of independent chains
> `Depth` Depth of each chain
> `Mean (us)` Average time per run (microseconds)
> `Median` Median time per run
> `Stddev` Standard deviation
> `Min`, `Max` Min/max run time
> `N` Number of measurements
> `ΔRSS(KB)` Change in process memory (KB)
> `ΔPeak(KB)` Change in peak RSS (KB)
> `PeakRSS(KB)` Max observed RSS memory (KB)
''';
return '$legend\n\n${([headerLine, divider] + lines).join('\n')}' ;
}
}

View File

@@ -0,0 +1,50 @@
import 'report_generator.dart';
/// Generates a human-readable, tab-delimited report for benchmark results.
///
/// Used for terminal and log output; shows each result as a single line with labeled headers.
class PrettyReport extends ReportGenerator {
/// List of columns to output in the pretty report.
@override
final List<String> keys = [
'benchmark','chainCount','nestingDepth','mean_us','median_us','stddev_us',
'min_us','max_us','trials','memory_diff_kb','delta_peak_kb','peak_rss_kb'
];
/// Mappings from internal benchmark IDs to display names.
static const nameMap = {
'Universal_UniversalBenchmark.registerSingleton': 'RegisterSingleton',
'Universal_UniversalBenchmark.chainSingleton': 'ChainSingleton',
'Universal_UniversalBenchmark.chainFactory': 'ChainFactory',
'Universal_UniversalBenchmark.chainAsync': 'AsyncChain',
'Universal_UniversalBenchmark.named': 'Named',
'Universal_UniversalBenchmark.override': 'Override',
};
/// Renders the results as a header + tab-separated value table.
@override
String render(List<Map<String, dynamic>> rows) {
final headers = [
'Benchmark', 'Chain Count', 'Depth', 'Mean (us)', 'Median', 'Stddev', 'Min', 'Max', 'N', 'ΔRSS(KB)', 'ΔPeak(KB)', 'PeakRSS(KB)'
];
final header = headers.join('\t');
final lines = rows.map((r) {
final readableName = nameMap[r['benchmark']] ?? r['benchmark'];
return [
readableName,
r['chainCount'],
r['nestingDepth'],
r['mean_us'],
r['median_us'],
r['stddev_us'],
r['min_us'],
r['max_us'],
r['trials'],
r['memory_diff_kb'],
r['delta_peak_kb'],
r['peak_rss_kb'],
].join('\t');
}).toList();
return ([header] + lines).join('\n');
}
}

View File

@@ -0,0 +1,9 @@
/// Abstract base for generating benchmark result reports in different formats.
///
/// Subclasses implement [render] to output results, and [keys] to define columns (if any).
abstract class ReportGenerator {
/// Renders the given [results] as a formatted string (table, markdown, csv, etc).
String render(List<Map<String, dynamic>> results);
/// List of output columns/keys included in the export (or [] for auto/all).
List<String> get keys;
}

View File

@@ -0,0 +1,96 @@
import 'dart:io';
import 'dart:math';
import 'package:benchmark_di/benchmarks/universal_chain_benchmark.dart';
import 'package:benchmark_di/benchmarks/universal_chain_async_benchmark.dart';
/// Holds the results for a single benchmark execution.
class BenchmarkResult {
/// List of timings for each run (in microseconds).
final List<num> timings;
/// Difference in memory (RSS, in KB) after running.
final int memoryDiffKb;
/// Difference between peak RSS and initial RSS (in KB).
final int deltaPeakKb;
/// Peak RSS memory observed (in KB).
final int peakRssKb;
BenchmarkResult({
required this.timings,
required this.memoryDiffKb,
required this.deltaPeakKb,
required this.peakRssKb,
});
/// Computes a BenchmarkResult instance from run timings and memory data.
factory BenchmarkResult.collect({
required List<num> timings,
required List<int> rssValues,
required int memBefore,
}) {
final memAfter = ProcessInfo.currentRss;
final memDiffKB = ((memAfter - memBefore) / 1024).round();
final peakRss = [...rssValues, memBefore].reduce(max);
final deltaPeakKb = ((peakRss - memBefore) / 1024).round();
return BenchmarkResult(
timings: timings,
memoryDiffKb: memDiffKB,
deltaPeakKb: deltaPeakKb,
peakRssKb: (peakRss / 1024).round(),
);
}
}
/// Static methods to execute and time benchmarks for DI containers.
class BenchmarkRunner {
/// Runs a synchronous benchmark ([UniversalChainBenchmark]) for a given number of [warmups] and [repeats].
/// Collects execution time and observed memory.
static Future<BenchmarkResult> runSync({
required UniversalChainBenchmark benchmark,
required int warmups,
required int repeats,
}) async {
final timings = <num>[];
final rssValues = <int>[];
for (int i = 0; i < warmups; i++) {
benchmark.setup();
benchmark.run();
benchmark.teardown();
}
final memBefore = ProcessInfo.currentRss;
for (int i = 0; i < repeats; i++) {
benchmark.setup();
final sw = Stopwatch()..start();
benchmark.run();
sw.stop();
timings.add(sw.elapsedMicroseconds);
rssValues.add(ProcessInfo.currentRss);
benchmark.teardown();
}
return BenchmarkResult.collect(timings: timings, rssValues: rssValues, memBefore: memBefore);
}
/// Runs an asynchronous benchmark ([UniversalChainAsyncBenchmark]) for a given number of [warmups] and [repeats].
/// Collects execution time and observed memory.
static Future<BenchmarkResult> runAsync({
required UniversalChainAsyncBenchmark benchmark,
required int warmups,
required int repeats,
}) async {
final timings = <num>[];
final rssValues = <int>[];
for (int i = 0; i < warmups; i++) {
await benchmark.setup();
await benchmark.run();
await benchmark.teardown();
}
final memBefore = ProcessInfo.currentRss;
for (int i = 0; i < repeats; i++) {
await benchmark.setup();
final sw = Stopwatch()..start();
await benchmark.run();
sw.stop();
timings.add(sw.elapsedMicroseconds);
rssValues.add(ProcessInfo.currentRss);
await benchmark.teardown();
}
return BenchmarkResult.collect(timings: timings, rssValues: rssValues, memBefore: memBefore);
}
}

View File

@@ -0,0 +1,188 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:benchmark_di/scenarios/universal_service.dart';
import 'package:cherrypick/cherrypick.dart';
import 'di_adapter.dart';
/// Test module that generates a chain of service bindings for benchmarking.
///
/// Configurable by chain count, nesting depth, binding mode, and scenario
/// to support various DI performance tests (singleton, factory, async, etc).
class UniversalChainModule extends Module {
/// Number of chains to create.
final int chainCount;
/// Depth of each chain.
final int nestingDepth;
/// How modules are registered (factory/singleton/async).
final UniversalBindingMode bindingMode;
/// Which di scenario to generate (chained, named, etc).
final UniversalScenario scenario;
/// Constructs a configured test DI module for the benchmarks.
UniversalChainModule({
required this.chainCount,
required this.nestingDepth,
this.bindingMode = UniversalBindingMode.singletonStrategy,
this.scenario = UniversalScenario.chain,
});
@override
void builder(Scope currentScope) {
if (scenario == UniversalScenario.asyncChain) {
// Generate async chain with singleton async bindings.
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';
bind<UniversalService>()
.toProvideAsync(() async {
final prev = level > 1
? await currentScope.resolveAsync<UniversalService>(named: prevDepName)
: null;
return UniversalServiceImpl(
value: depName,
dependency: prev,
);
})
.withName(depName)
.singleton();
}
}
return;
}
switch (scenario) {
case UniversalScenario.register:
// Simple singleton registration.
bind<UniversalService>()
.toProvide(() => UniversalServiceImpl(value: 'reg', dependency: null))
.singleton();
break;
case UniversalScenario.named:
// Named factory registration for two distinct objects.
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.
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<UniversalService>()
.toProvide(() => UniversalServiceImpl(
value: depName,
dependency: currentScope.tryResolve<UniversalService>(named: prevDepName),
))
.withName(depName)
.singleton();
break;
case UniversalBindingMode.factoryStrategy:
bind<UniversalService>()
.toProvide(() => UniversalServiceImpl(
value: depName,
dependency: currentScope.tryResolve<UniversalService>(named: prevDepName),
))
.withName(depName);
break;
case UniversalBindingMode.asyncStrategy:
bind<UniversalService>()
.toProvideAsync(() async => UniversalServiceImpl(
value: depName,
dependency: await currentScope.resolveAsync<UniversalService>(named: prevDepName),
))
.withName(depName)
.singleton();
break;
}
}
}
// Регистрация алиаса без имени (на последний элемент цепочки)
final depName = '${chainCount}_$nestingDepth';
bind<UniversalService>()
.toProvide(() => currentScope.resolve<UniversalService>(named: depName))
.singleton();
break;
case UniversalScenario.override:
// 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
break;
}
}
}
class CherrypickDIAdapter extends DIAdapter<Scope> {
Scope? _scope;
final bool _isSubScope;
CherrypickDIAdapter([Scope? scope, this._isSubScope = false]) {
_scope = scope;
}
@override
void setupDependencies(void Function(Scope container) registration) {
_scope ??= CherryPick.openRootScope();
registration(_scope!);
}
@override
Registration<Scope> universalRegistration<S extends Enum>({
required S scenario,
required int chainCount,
required int nestingDepth,
required UniversalBindingMode bindingMode,
}) {
if (scenario is UniversalScenario) {
return (scope) {
scope.installModules([
UniversalChainModule(
chainCount: chainCount,
nestingDepth: nestingDepth,
bindingMode: bindingMode,
scenario: scenario,
),
]);
};
}
throw UnsupportedError('Scenario $scenario not supported by CherrypickDIAdapter');
}
@override
T resolve<T extends Object>({String? named}) =>
_scope!.resolve<T>(named: named);
@override
Future<T> resolveAsync<T extends Object>({String? named}) async =>
_scope!.resolveAsync<T>(named: named);
@override
void teardown() {
if (!_isSubScope) {
CherryPick.closeRootScope();
_scope = null;
}
// SubScope teardown не требуется
}
@override
CherrypickDIAdapter openSubScope(String name) {
return CherrypickDIAdapter(_scope!.openSubScope(name), true);
}
@override
Future<void> waitForAsyncReady() async {}
}

View File

@@ -0,0 +1,32 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
/// Универсальная абстракция для DI-адаптера с унифицированной функцией регистрации.
/// Теперь для каждого адаптера задаём строгий generic тип контейнера.
typedef Registration<TContainer> = void Function(TContainer);
abstract class DIAdapter<TContainer> {
/// Устанавливает зависимости с помощью строго типизированного контейнера.
void setupDependencies(void Function(TContainer container) registration);
/// Возвращает типобезопасную функцию регистрации зависимостей под конкретный сценарий.
Registration<TContainer> universalRegistration<S extends Enum>({
required S scenario,
required int chainCount,
required int nestingDepth,
required UniversalBindingMode bindingMode,
});
/// Резолвит (возвращает) экземпляр типа [T] (по имени, если требуется).
T resolve<T extends Object>({String? named});
/// Асинхронно резолвит экземпляр типа [T] (если нужно).
Future<T> resolveAsync<T extends Object>({String? named});
/// Уничтожает/отчищает DI-контейнер.
void teardown();
/// Открывает дочерний scope и возвращает новый адаптер (если поддерживается).
DIAdapter<TContainer> openSubScope(String name);
/// Ожидание готовности DI контейнера (если нужно для async DI).
Future<void> waitForAsyncReady() async {}
}

View File

@@ -0,0 +1,156 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:benchmark_di/scenarios/universal_service.dart';
import 'package:get_it/get_it.dart';
import 'di_adapter.dart';
/// Универсальный DIAdapter для GetIt c поддержкой scopes и строгой типизацией.
class GetItAdapter extends DIAdapter<GetIt> {
late GetIt _getIt;
final String? _scopeName;
final bool _isSubScope;
bool _scopePushed = false;
/// Основной (root) и subScope-конструкторы.
GetItAdapter({GetIt? instance, String? scopeName, bool isSubScope = false})
: _scopeName = scopeName,
_isSubScope = isSubScope {
if (instance != null) {
_getIt = instance;
}
}
@override
void setupDependencies(void Function(GetIt container) registration) {
if (_isSubScope) {
// Создаём scope через pushNewScope с init
_getIt.pushNewScope(
scopeName: _scopeName,
init: (getIt) => registration(getIt),
);
_scopePushed = true;
} else {
_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() {
if (_isSubScope && _scopePushed) {
_getIt.popScope();
_scopePushed = false;
} else {
_getIt.reset();
}
}
@override
GetItAdapter openSubScope(String name) =>
GetItAdapter(instance: _getIt, scopeName: name, isSubScope: true);
@override
Future<void> waitForAsyncReady() async {
await _getIt.allReady();
}
@override
Registration<GetIt> universalRegistration<S extends Enum>({
required S scenario,
required int chainCount,
required int nestingDepth,
required UniversalBindingMode bindingMode,
}) {
if (scenario is UniversalScenario) {
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.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;
}
if (scenario == UniversalScenario.chain || scenario == UniversalScenario.override) {
final depName = '${chainCount}_$nestingDepth';
getIt.registerSingleton<UniversalService>(
getIt<UniversalService>(instanceName: depName),
);
}
};
}
throw UnsupportedError('Scenario $scenario not supported by GetItAdapter');
}
}

View File

@@ -0,0 +1,139 @@
import 'package:benchmark_di/scenarios/universal_binding_mode.dart';
import 'package:benchmark_di/scenarios/universal_scenario.dart';
import 'package:benchmark_di/scenarios/universal_service.dart';
import 'package:riverpod/riverpod.dart' as rp;
import 'di_adapter.dart';
/// Унифицированный DIAdapter для Riverpod с поддержкой scopes и строгой типизацией.
class RiverpodAdapter extends DIAdapter<Map<String, rp.ProviderBase<Object?>>> {
rp.ProviderContainer? _container;
final Map<String, rp.ProviderBase<Object?>> _namedProviders;
final rp.ProviderContainer? _parent;
final bool _isSubScope;
RiverpodAdapter({
rp.ProviderContainer? container,
Map<String, rp.ProviderBase<Object?>>? providers,
rp.ProviderContainer? parent,
bool isSubScope = false,
}) : _container = container,
_namedProviders = providers ?? <String, rp.ProviderBase<Object?>>{},
_parent = parent,
_isSubScope = isSubScope;
@override
void setupDependencies(void Function(Map<String, rp.ProviderBase<Object?>> container) registration) {
_container ??= _parent == null
? rp.ProviderContainer()
: rp.ProviderContainer(parent: _parent);
registration(_namedProviders);
}
@override
T resolve<T extends Object>({String? named}) {
final key = named ?? T.toString();
final provider = _namedProviders[key];
if (provider == null) {
throw Exception('Provider not found for $key');
}
return _container!.read(provider) as T;
}
@override
Future<T> resolveAsync<T extends Object>({String? named}) async {
final key = named ?? T.toString();
final provider = _namedProviders[key];
if (provider == null) {
throw Exception('Provider not found for $key');
}
// Если это FutureProvider — используем .future
if (provider.runtimeType.toString().contains('FutureProvider')) {
return await _container!.read((provider as dynamic).future) as T;
}
return resolve<T>(named: named);
}
@override
void teardown() {
_container?.dispose();
_container = null;
_namedProviders.clear();
}
@override
RiverpodAdapter openSubScope(String name) {
final newContainer = rp.ProviderContainer(parent: _container);
return RiverpodAdapter(
container: newContainer,
providers: Map.of(_namedProviders),
parent: _container,
isSubScope: true,
);
}
@override
Future<void> waitForAsyncReady() async {
// Riverpod синхронный по умолчанию.
return;
}
@override
Registration<Map<String, rp.ProviderBase<Object?>>> universalRegistration<S extends Enum>({
required S scenario,
required int chainCount,
required int nestingDepth,
required UniversalBindingMode bindingMode,
}) {
if (scenario is UniversalScenario) {
return (providers) {
switch (scenario) {
case UniversalScenario.register:
providers['UniversalService'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: 'reg', dependency: null));
break;
case UniversalScenario.named:
providers['impl1'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: 'impl1'));
providers['impl2'] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(value: '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';
providers[depName] = rp.Provider<UniversalService>((ref) => UniversalServiceImpl(
value: depName,
dependency: level > 1 ? ref.watch(providers[prevDepName] as rp.ProviderBase<UniversalService>) : null,
));
}
}
final depName = '${chainCount}_$nestingDepth';
providers['UniversalService'] = rp.Provider<UniversalService>((ref) => ref.watch(providers[depName] as rp.ProviderBase<UniversalService>));
break;
case UniversalScenario.override:
// handled at benchmark level
break;
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';
providers[depName] = rp.FutureProvider<UniversalService>((ref) async {
return UniversalServiceImpl(
value: depName,
dependency: level > 1
? await ref.watch((providers[prevDepName] as rp.FutureProvider<UniversalService>).future) as UniversalService?
: null,
);
});
}
}
final depName = '${chainCount}_$nestingDepth';
providers['UniversalService'] = rp.FutureProvider<UniversalService>((ref) async {
return await ref.watch((providers[depName] as rp.FutureProvider<UniversalService>).future);
});
break;
}
};
}
throw UnsupportedError('Scenario $scenario not supported by RiverpodAdapter');
}
}

View File

@@ -0,0 +1,11 @@
/// Enum to represent the DI registration/binding mode.
enum UniversalBindingMode {
/// Singleton/provider binding.
singletonStrategy,
/// Factory-based binding.
factoryStrategy,
/// Async-based binding.
asyncStrategy,
}

View File

@@ -0,0 +1,13 @@
/// Enum to represent which scenario is constructed for the benchmark.
enum UniversalScenario {
/// Single registration.
register,
/// Chain of dependencies.
chain,
/// Named registrations.
named,
/// Child-scope override scenario.
override,
/// Asynchronous chain scenario.
asyncChain,
}

View File

@@ -0,0 +1,17 @@
/// Base interface for any universal service in the benchmarks.
///
/// Represents an object in the dependency chain with an identifiable value
/// and (optionally) a dependency on a previous service in the chain.
abstract class UniversalService {
/// String ID for this service instance (e.g. chain/level info).
final String value;
/// Optional reference to dependency service in the chain.
final UniversalService? dependency;
UniversalService({required this.value, this.dependency});
}
/// Default implementation for [UniversalService] used in service chains.
class UniversalServiceImpl extends UniversalService {
UniversalServiceImpl({required super.value, super.dependency});
}

View File

@@ -9,6 +9,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.4"
args:
dependency: "direct main"
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
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:
@@ -31,7 +47,15 @@ packages:
path: "../cherrypick"
relative: true
source: path
version: "3.0.0-dev.1"
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:
@@ -40,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:
@@ -64,5 +96,37 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
riverpod:
dependency: "direct main"
description:
name: riverpod
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
url: "https://pub.dev"
source: hosted
version: "2.6.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
url: "https://pub.dev"
source: hosted
version: "1.0.0"
sdks:
dart: ">=3.6.0 <4.0.0"

View File

@@ -1,7 +1,7 @@
name: benchmark_cherrypick
name: benchmark_di
version: 0.1.0
publish_to: none
description: Benchmark for cherrypick core DI library
description: Universal benchmark for any DI library (cherrypick, get_it, and others)
environment:
sdk: '>=3.0.0 <4.0.0'
@@ -9,6 +9,9 @@ environment:
dependencies:
cherrypick:
path: ../cherrypick
args: ^2.7.0
get_it: ^8.2.0
riverpod: ^2.6.1
dev_dependencies:
lints: ^5.0.0

View File

@@ -3,7 +3,7 @@ name: cherrypick_workspace
sdkPath: .fvm/flutter_sdk
packages:
- benchmark_cherrypick
- benchmark_di
- cherrypick
- cherrypick_flutter
- cherrypick_annotations

View File

@@ -5,23 +5,23 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
version: "73.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "0.3.2"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "6.8.0"
ansi_styles:
dependency: transitive
description:
@@ -298,10 +298,10 @@ packages:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
version: "0.1.2-main.4"
matcher:
dependency: transitive
description: