feat: full di benchmarks report (en/ru) + get_it scope+override support fix; fresh results for all scenarios and settings

This commit is contained in:
Sergey Penkovsky
2025-08-07 12:11:16 +03:00
parent 6b6564f8c3
commit f7a7ea4384
4 changed files with 212 additions and 3 deletions

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

@@ -21,8 +21,52 @@ class GetItAdapter implements DIAdapter {
@override
DIAdapter openSubScope(String name) {
// get_it не поддерживает scope, возвращаем новый инстанс
return GetItAdapter();
// Открываем новый scope и возвращаем адаптер, который в setupDependencies будет использовать init.
return _GetItScopeAdapter(_getIt, name);
}
@override
Future<void> waitForAsyncReady() async {
await _getIt.allReady();
}
}
class _GetItScopeAdapter implements DIAdapter {
final GetIt _getIt;
final String _scopeName;
bool _scopePushed = false;
void Function(dynamic container)? _pendingRegistration;
_GetItScopeAdapter(this._getIt, this._scopeName);
@override
void setupDependencies(void Function(dynamic container) registration) {
_pendingRegistration = registration;
// Создаём scope через pushNewScope с init для правильной регистрации
_getIt.pushNewScope(
scopeName: _scopeName,
init: (getIt) => _pendingRegistration?.call(getIt),
);
_scopePushed = true;
}
@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 (_scopePushed) {
_getIt.popScope();
_scopePushed = false;
}
}
@override
DIAdapter openSubScope(String name) {
return _GetItScopeAdapter(_getIt, name);
}
@override

View File

@@ -28,7 +28,7 @@ void Function(dynamic) getUniversalRegistration(
),
]);
};
} else if (adapter is GetItAdapter) {
} else if (adapter is GetItAdapter || adapter.runtimeType.toString().contains('GetItScopeAdapter')) {
return (getIt) {
switch (scenario) {
case UniversalScenario.asyncChain:
@@ -103,6 +103,13 @@ void Function(dynamic) getUniversalRegistration(
// handled at benchmark level
break;
}
// UniversalService alias (без имени) для chain/override-сценариев
if (scenario == UniversalScenario.chain || scenario == UniversalScenario.override) {
final depName = '${chainCount}_$nestingDepth';
getIt.registerSingleton<UniversalService>(
getIt<UniversalService>(instanceName: depName),
);
}
};
}
throw UnsupportedError('Unknown DIAdapter type: ${adapter.runtimeType}');