Ранее опубликовал сравнение роутинга для Slim и HLEB2 при большом количестве маршрутов (иначе не было видно заметных различий), теперь пришёл черед реализации контейнера. Как и в прошлый раз получается, в Slim контейнер сторонний нужно установить, на выбор. Выбор пал на контейнер Symfony, так как слышал о нём лестные отзывы, что он без проблем может содержать несколько тысяч зарегистрированных сущностей и работать нормально. Принципы этой регистрации по сравнению с фреймворком HLEB2 совершенно разные, следующий эксперимент покажет, какой из контейнеров справится с нагрузкой лучше. Во фреймворке HLEB2 по сути никакой "регистрации" при этом нет, так как промежуточные конфигурации и ООП-инициализация заменены на нативный php-код, разобраться в котором проще и работает он быстрее.
Для испытаний взял последнюю на данный момент версию Slim Framework (v4.13.0) и HLEB2 (v2.0.12). Дополнительно для SLim была установлена библиотека symfony/dependency-injection (v7.0.7).
Slim
В первую очередь нужно было сгенерировать нужное количество классов, чтобы затем в цикле зарегистрировать в контейнере. С этим справился небольшой самописный php-скрипт, который создал в папке App/Services/ классы вроде этого (от CurrentDate1Service и выше):
<?php
namespace App\Services;
class CurrentDate100Service
{
public function run(): int
{
return time();
}
}
Затем эти классы зарегистрировал в контейнере:
<?php
use Slim\Factory\AppFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
require __DIR__ . '/../vendor/autoload.php';
$container = new ContainerBuilder();
for ($i = 1; $i < 101; $i++) {
$container->register("current_date_{$i}_service", "App\Services\CurrentDate{$i}Service");
}
AppFactory::setContainer($container);
$app = AppFactory::create();
$app->get('/', function ($request, $response) {
$service = $this->get('current_date_100_service');
$response->getBody()->write((string)$service->run());
return $response;
});
$app->run();
Теперь осталось расставить замеры производительности и сделать запрос к странице. Далее можно повышать до тысячи и больше.
HLEB2
Здесь, чтобы имитировать запрос к контейнеру, нужно было создать контроллер, так как из самого роутинга нельзя вернуть динамический результат (маршруты кешируются). Контроллер такого вида:
<?php
namespace App\Controllers;
use App\Bootstrap\Services\CurrentDate100Service;
use Hleb\Base\Controller;
class DefaultController extends Controller
{
public function index(): int
{
return $this->container->get(CurrentDate100Service::class)->run();
}
}
Затем похожим php-скриптом были созданы классы сервисов аналогично как для Slim:
<?php
namespace App\Bootstrap\Services;
class CurrentDate100Service
{
public function get(): int
{
return time();
}
}
Этим же скриптом был создан файл с расширением php, представляющий из себя оператор match с большим количеством отношений (например "\App\Bootstrap\Services\CurrentDate100Service::class => new \App\Bootstrap\Services\CurrentDate100Service,") и возвращающий результат. Затем этот файл через require был помещён в App\Bootstrap\ContainerFactory заменив исходное выражение match. Эта манипуляция позволила использовать отношения максимально приближенно к реальным условиям. В маршрутах осталось указать контроллер:
Route::get('/')->controller(\App\Controllers\DefaultController::class);
Результат
Для теста запрашивалась последняя зарегистрированная сущность по id. Увеличивая кратность в десять раз, получилась такая таблица:

Из неё видно, что библиотека symfony прекрасно справилась с поиском по 10 тысячами сущностей в контейнере, но, ведь запрашивался только один объект, а в приложениях даже среднего размера таких обращений к различным вложениям контейнера могут быть десятки. В этом случае видно преимущество устройства контейнера HLEB2, который не заметил большого количества добавленных сущностей и разницы между сотней и десятью тысячами тестовых единиц особой нет.