diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md index 7624df93fa..e4a7ea861d 100644 --- a/CHANGELOG-5.0.md +++ b/CHANGELOG-5.0.md @@ -19,6 +19,7 @@ All notable changes are documented here. The format is based on [Keep a Changelo ### Added +- Benchmark subjects for Acl, Cache, Config, Flash, Forms, Jwt, Logger, Security, Session and Validation in `tests/benchmarks`. [#17695](https://github.com/phalcon/cphalcon/issues/17695) ### Fixed diff --git a/tests/benchmarks/Acl/AclBench.php b/tests/benchmarks/Acl/AclBench.php new file mode 100644 index 0000000000..c471f32c40 --- /dev/null +++ b/tests/benchmarks/Acl/AclBench.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Acl; + +use Phalcon\Acl\Adapter\Memory; +use Phalcon\Di\Di; +use PhpBench\Attributes\BeforeMethods; +use PhpBench\Attributes\Revs; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class AclBench +{ + private const ACCESS = ['index', 'view', 'edit', 'delete']; + + private const COMPONENTS = ['posts', 'comments', 'users', 'settings', 'reports']; + + private Memory $acl; + + public function setUp(): void + { + Di::reset(); + + $this->acl = $this->buildAcl(); + } + + /** + * The ACL with 4 roles and 5 components, as one request makes it. + */ + #[Revs(200)] + public function benchBuild(): void + { + $acl = $this->buildAcl(); + + if (true !== $acl->isAllowed('admin', 'settings', 'delete')) { + throw new RuntimeException('Unexpected value: false'); + } + } + + public function benchIsAllowed(): void + { + if (true !== $this->acl->isAllowed('editor', 'posts', 'edit')) { + throw new RuntimeException('Unexpected value: false'); + } + } + + /** + * The access comes from guest, through user (editor inherits user). + */ + public function benchIsAllowedInherited(): void + { + if (true !== $this->acl->isAllowed('editor', 'comments', 'view')) { + throw new RuntimeException('Unexpected value: false'); + } + } + + public function benchIsDenied(): void + { + if (false !== $this->acl->isAllowed('guest', 'users', 'delete')) { + throw new RuntimeException('Unexpected value: true'); + } + } + + private function buildAcl(): Memory + { + $acl = new Memory(); + + $acl->addRole('guest'); + $acl->addRole('user', 'guest'); + $acl->addRole('editor', 'user'); + $acl->addRole('admin', 'editor'); + + foreach (self::COMPONENTS as $component) { + $acl->addComponent($component, self::ACCESS); + } + + $acl->allow('guest', 'posts', ['index', 'view']); + $acl->allow('guest', 'comments', ['index', 'view']); + $acl->allow('user', 'comments', 'edit'); + $acl->allow('editor', 'posts', ['edit', 'delete']); + $acl->allow('admin', '*', '*'); + + return $acl; + } +} diff --git a/tests/benchmarks/Cache/CacheBench.php b/tests/benchmarks/Cache/CacheBench.php new file mode 100644 index 0000000000..7aa90f5423 --- /dev/null +++ b/tests/benchmarks/Cache/CacheBench.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Cache; + +use Phalcon\Cache\AdapterFactory; +use Phalcon\Cache\Cache; +use Phalcon\Di\Di; +use Phalcon\Storage\SerializerFactory; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class CacheBench +{ + private const ROBOTS = ['one', 'two', 'three']; + + private Cache $cache; + + private int $counter = 0; + + public function setUp(): void + { + Di::reset(); + + $this->cache = $this->newCache(); + $this->cache->set('robots', self::ROBOTS); + } + + /** + * The two factories, the memory adapter and the cache, as one request + * makes them. + */ + public function benchCreate(): void + { + $cache = $this->newCache(); + + if (false !== $cache->has('missing')) { + throw new RuntimeException('Unexpected value: true'); + } + } + + public function benchGet(): void + { + $actual = $this->cache->get('robots'); + + if (self::ROBOTS !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: %s', var_export($actual, true))); + } + } + + /** + * set() of a key that exists, with a new value on each call. + */ + public function benchSet(): void + { + $this->counter++; + + if (true !== $this->cache->set('counter', $this->counter)) { + throw new RuntimeException('Unexpected value: false'); + } + } + + private function newCache(): Cache + { + $factory = new AdapterFactory(new SerializerFactory()); + + return new Cache($factory->newInstance('memory')); + } +} diff --git a/tests/benchmarks/Config/ConfigBench.php b/tests/benchmarks/Config/ConfigBench.php new file mode 100644 index 0000000000..856188654b --- /dev/null +++ b/tests/benchmarks/Config/ConfigBench.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Config; + +use Phalcon\Config\Config; +use Phalcon\Di\Di; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class ConfigBench +{ + private const DATA = [ + 'app' => [ + 'baseUri' => '/', + 'name' => 'bench', + 'timezone' => 'UTC', + ], + 'database' => [ + 'adapter' => 'mysql', + 'dbname' => 'phalcon', + 'host' => 'localhost', + 'options' => [ + 'charset' => 'utf8mb4', + ], + 'password' => 'secret', + 'port' => 3306, + 'username' => 'root', + ], + 'services' => [ + 'cache' => 'memory', + 'logger' => 'stream', + 'mailer' => 'smtp', + 'queue' => 'beanstalk', + 'session' => 'files', + ], + ]; + + private Config $config; + + public function setUp(): void + { + Di::reset(); + + $this->config = new Config(self::DATA); + } + + /** + * A new config from a nested array, as one request makes it: each + * nested array becomes a Config object. + */ + public function benchConstruct(): void + { + $config = new Config(self::DATA); + + $actual = $config->path('database.options.charset'); + if ('utf8mb4' !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: %s', var_export($actual, true))); + } + } + + public function benchPath(): void + { + $actual = $this->config->path('database.host'); + + if ('localhost' !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: %s', var_export($actual, true))); + } + } +} diff --git a/tests/benchmarks/Encryption/JwtBench.php b/tests/benchmarks/Encryption/JwtBench.php new file mode 100644 index 0000000000..d8a755efbf --- /dev/null +++ b/tests/benchmarks/Encryption/JwtBench.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Encryption; + +use Phalcon\Di\Di; +use Phalcon\Encryption\Security\JWT\Builder; +use Phalcon\Encryption\Security\JWT\Signer\Hmac; +use Phalcon\Encryption\Security\JWT\Token\Parser; +use PhpBench\Attributes\BeforeMethods; +use PhpBench\Attributes\Revs; +use RuntimeException; + +/** + * The time claims are fixed, so the token is the same on each call. + */ +#[BeforeMethods('setUp')] +#[Revs(200)] +final class JwtBench +{ + private const EXPECTED_TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9' + . '.eyJhdWQiOlsiaHR0cHM6Ly9waGFsY29uLmlvIl0sImV4cCI6NDEwMjQ0NDgwMCwianRpIjoiYmVuY2gtMSIsImlhdCI6' + . 'MTcwMDAwMDAwMCwiaXNzIjoiaHR0cHM6Ly9waGFsY29uLmlvIiwibmJmIjoxNzAwMDAwMDAwLCJzdWIiOiJiZW5jaCJ9' + . '.F_CWXOsTMdZz_bXLYj9MxwpfcpXUNZrPCob1zR0AHwWqoHr2yNNTVgji7L7AfVnWAXtxcEn0usXWdxwkKVE3rQ'; + + private const PASSPHRASE = 'Phalcon!Bench#Passphrase2026'; + + private Parser $parser; + + private Hmac $signer; + + public function setUp(): void + { + Di::reset(); + + $this->parser = new Parser(); + $this->signer = new Hmac(); + } + + public function benchBuild(): void + { + $builder = new Builder($this->signer); + $builder + ->setAudience('https://phalcon.io') + ->setExpirationTime(4102444800) + ->setId('bench-1') + ->setIssuedAt(1700000000) + ->setIssuer('https://phalcon.io') + ->setNotBefore(1700000000) + ->setPassphrase(self::PASSPHRASE) + ->setSubject('bench'); + + $actual = $builder->getToken()->getToken(); + + if (self::EXPECTED_TOKEN !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: "%s"', $actual)); + } + } + + public function benchParse(): void + { + $actual = $this->parser->parse(self::EXPECTED_TOKEN)->getClaims()->get('sub'); + + if ('bench' !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: %s', var_export($actual, true))); + } + } +} diff --git a/tests/benchmarks/Encryption/SecurityBench.php b/tests/benchmarks/Encryption/SecurityBench.php new file mode 100644 index 0000000000..ef08e36596 --- /dev/null +++ b/tests/benchmarks/Encryption/SecurityBench.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Encryption; + +use Phalcon\Di\Di; +use Phalcon\Encryption\Security; +use Phalcon\Session\Adapter\Noop; +use Phalcon\Session\Manager; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class SecurityBench +{ + private Manager $session; + + private string $token = ''; + + private string $tokenKey = ''; + + public function setUp(): void + { + Di::reset(); + + $this->session = new Manager(); + $this->session->setAdapter(new Noop()); + $this->session->start(); + + if (PHP_SESSION_ACTIVE !== $this->session->status()) { + throw new RuntimeException( + sprintf('Unexpected session status: %d', $this->session->status()) + ); + } + + $this->newToken(new Security($this->session)); + } + + /** + * One form request: check the CSRF token of the previous call, then make + * a new key and token for the next form. checkToken() removes the + * checked token from the session. + */ + public function benchCsrf(): void + { + $security = new Security($this->session); + + if (true !== $security->checkToken($this->tokenKey, $this->token)) { + throw new RuntimeException(sprintf('Unexpected value: false for token "%s"', $this->token)); + } + + $this->newToken($security); + } + + private function newToken(Security $security): void + { + $this->tokenKey = (string) $security->getTokenKey(); + $this->token = (string) $security->getToken(); + } +} diff --git a/tests/benchmarks/Filter/ValidationBench.php b/tests/benchmarks/Filter/ValidationBench.php new file mode 100644 index 0000000000..845d6edb7e --- /dev/null +++ b/tests/benchmarks/Filter/ValidationBench.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Filter; + +use Phalcon\Di\Di; +use Phalcon\Filter\Validation; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; +use Phalcon\Filter\Validation\Validator\StringLength; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class ValidationBench +{ + /** + * The name is too short (StringLength) and the email is not valid + * (Email): 2 messages. + */ + private const INVALID_DATA = [ + 'email' => 'bad', + 'name' => 'a', + ]; + + private const VALID_DATA = [ + 'email' => 'team@phalcon.io', + 'name' => 'Phalcon', + ]; + + private Validation $validation; + + public function setUp(): void + { + Di::reset(); + + $this->validation = new Validation(); + $this->validation->add('name', new PresenceOf()); + $this->validation->add('email', new PresenceOf()); + $this->validation->add('email', new Email()); + $this->validation->add('name', new StringLength(['max' => 50, 'min' => 2])); + } + + public function benchValidate(): void + { + $messages = $this->validation->validate(self::VALID_DATA); + + if (0 !== count($messages)) { + throw new RuntimeException(sprintf('Unexpected count: %d', count($messages))); + } + } + + public function benchValidateFails(): void + { + $messages = $this->validation->validate(self::INVALID_DATA); + + if (2 !== count($messages)) { + throw new RuntimeException(sprintf('Unexpected count: %d', count($messages))); + } + } +} diff --git a/tests/benchmarks/Flash/FlashBench.php b/tests/benchmarks/Flash/FlashBench.php new file mode 100644 index 0000000000..ce10457115 --- /dev/null +++ b/tests/benchmarks/Flash/FlashBench.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Flash; + +use Phalcon\Di\Di; +use Phalcon\Flash\Direct; +use Phalcon\Html\Escaper; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class FlashBench +{ + private const EXPECTED_OUTPUT = '
Robot <b>saved</b>
' . PHP_EOL + . '
Robot <b>not saved</b>
' . PHP_EOL + . '
Robot <b>changed</b>
' . PHP_EOL + . '
Robot <b>old</b>
' . PHP_EOL; + + private Direct $flash; + + public function setUp(): void + { + Di::reset(); + + $this->flash = new Direct(new Escaper()); + $this->flash->setImplicitFlush(false); + } + + /** + * 4 messages, one of each type. Each text has HTML, so the escaper runs. + */ + public function benchMessages(): void + { + $actual = $this->flash->success('Robot saved') + . $this->flash->error('Robot not saved') + . $this->flash->notice('Robot changed') + . $this->flash->warning('Robot old'); + + if (self::EXPECTED_OUTPUT !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: "%s"', $actual)); + } + } +} diff --git a/tests/benchmarks/Forms/FormBench.php b/tests/benchmarks/Forms/FormBench.php new file mode 100644 index 0000000000..6694ff3540 --- /dev/null +++ b/tests/benchmarks/Forms/FormBench.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Forms; + +use Phalcon\Di\Di; +use Phalcon\Filter\Validation\Validator\Email as EmailValidator; +use Phalcon\Filter\Validation\Validator\PresenceOf; +use Phalcon\Filter\Validation\Validator\StringLength; +use Phalcon\Forms\Element\Email; +use Phalcon\Forms\Element\Password; +use Phalcon\Forms\Element\Select; +use Phalcon\Forms\Element\Text; +use Phalcon\Forms\Element\TextArea; +use Phalcon\Forms\Form; +use Phalcon\Html\Escaper; +use Phalcon\Html\TagFactory; +use PhpBench\Attributes\BeforeMethods; +use PhpBench\Attributes\Revs; +use RuntimeException; + +#[BeforeMethods('setUp')] +#[Revs(200)] +final class FormBench +{ + private const ELEMENTS = ['name', 'email', 'password', 'country', 'about']; + + private const EXPECTED_HTML = '' + . '' + . '' + . '' + . ''; + + private const VALID_DATA = [ + 'about' => 'A full stack PHP framework', + 'country' => 'gr', + 'email' => 'team@phalcon.io', + 'name' => 'Phalcon', + 'password' => 'secret123', + ]; + + private Form $form; + + private TagFactory $tagFactory; + + /** + * Select::render() uses Phalcon\Tag, which reads the 'escaper' service + * of the default container. The container has only this service. + */ + public function setUp(): void + { + Di::reset(); + + $container = new Di(); + $container->setShared('escaper', Escaper::class); + Di::setDefault($container); + + $this->tagFactory = new TagFactory(new Escaper()); + $this->form = $this->buildForm(); + } + + /** + * A form with 5 elements and their validators, as one request makes it. + */ + public function benchConstruct(): void + { + $form = $this->buildForm(); + + if (5 !== $form->count()) { + throw new RuntimeException(sprintf('Unexpected count: %d', $form->count())); + } + } + + public function benchIsValid(): void + { + if (true !== $this->form->isValid(self::VALID_DATA)) { + throw new RuntimeException('Unexpected value: false'); + } + } + + public function benchRender(): void + { + $actual = ''; + foreach (self::ELEMENTS as $name) { + $actual .= $this->form->render($name); + } + + if (self::EXPECTED_HTML !== $actual) { + throw new RuntimeException(sprintf('Unexpected value: "%s"', $actual)); + } + } + + private function buildForm(): Form + { + $name = new Text('name'); + $name->addValidators( + [ + new PresenceOf(), + new StringLength(['max' => 50, 'min' => 2]), + ] + ); + + $email = new Email('email'); + $email->addValidators( + [ + new PresenceOf(), + new EmailValidator(), + ] + ); + + $password = new Password('password'); + $password->addValidator(new StringLength(['min' => 8])); + + $country = new Select( + 'country', + [ + 'de' => 'Germany', + 'gr' => 'Greece', + 'us' => 'USA', + ] + ); + + $form = new Form(); + $form->setTagFactory($this->tagFactory); + $form->add($name); + $form->add($email); + $form->add($password); + $form->add($country); + $form->add(new TextArea('about')); + + return $form; + } +} diff --git a/tests/benchmarks/Logger/LastLineAdapter.php b/tests/benchmarks/Logger/LastLineAdapter.php new file mode 100644 index 0000000000..44929797b2 --- /dev/null +++ b/tests/benchmarks/Logger/LastLineAdapter.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Logger; + +use Phalcon\Logger\Adapter\AbstractAdapter; +use Phalcon\Logger\Item; + +/** + * A logger adapter with no I/O. It keeps the last formatted line, so the + * subjects can check it. + */ +final class LastLineAdapter extends AbstractAdapter +{ + public string $last = ''; + + public function close(): bool + { + return true; + } + + public function process(Item $item): void + { + $this->last = $this->getFormattedItem($item); + } +} diff --git a/tests/benchmarks/Logger/LoggerBench.php b/tests/benchmarks/Logger/LoggerBench.php new file mode 100644 index 0000000000..6f3e8a5070 --- /dev/null +++ b/tests/benchmarks/Logger/LoggerBench.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Benchmarks\Logger; + +use Phalcon\Di\Di; +use Phalcon\Logger\Enum; +use Phalcon\Logger\Logger; +use PhpBench\Attributes\BeforeMethods; +use RuntimeException; + +#[BeforeMethods('setUp')] +final class LoggerBench +{ + private const CONTEXT = ['name' => 'phalcon']; + + private const MESSAGE = 'User %name% logged in'; + + private LastLineAdapter $adapter; + + private Logger $logger; + + private LastLineAdapter $skippedAdapter; + + private Logger $skippedLogger; + + public function setUp(): void + { + Di::reset(); + + $this->adapter = new LastLineAdapter(); + $this->logger = new Logger('bench', ['main' => $this->adapter]); + + $this->skippedAdapter = new LastLineAdapter(); + $this->skippedLogger = new Logger('skipped', ['main' => $this->skippedAdapter]); + $this->skippedLogger->setLogLevel(Enum::ERROR); + } + + /** + * debug() on a logger with the level ERROR: no line is written. + */ + public function benchDebugSkipped(): void + { + $this->skippedLogger->debug(self::MESSAGE, self::CONTEXT); + + if ('' !== $this->skippedAdapter->last) { + throw new RuntimeException(sprintf('Unexpected value: "%s"', $this->skippedAdapter->last)); + } + } + + /** + * info() with one placeholder, formatted with the default line + * formatter (date, level, message). + */ + public function benchInfo(): void + { + $this->logger->info(self::MESSAGE, self::CONTEXT); + + if (!str_ends_with($this->adapter->last, 'User phalcon logged in')) { + throw new RuntimeException(sprintf('Unexpected value: "%s"', $this->adapter->last)); + } + } +} diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md index cd1f263db0..7ee2859a74 100644 --- a/tests/benchmarks/README.md +++ b/tests/benchmarks/README.md @@ -86,23 +86,35 @@ docker exec cphalcon-bench-8.4 callgrind_annotate --auto=no --inclusive=yes /srv ## Micro subjects -One subject for each component entry point that the reference apps run (selected with the coverage data). +One subject for each component entry point that the reference apps run (selected with the coverage data), and +subjects for components that no reference app uses (Acl, Cache, Config, Encryption, Flash, Forms, Logger, +Session and Validation). | Class | Subjects | |----------------------------|--------------------------------------------------------------------------------| +| `Acl\AclBench` | `benchBuild`, `benchIsAllowed`, `benchIsAllowedInherited`, `benchIsDenied` | +| `Cache\CacheBench` | `benchCreate`, `benchGet`, `benchSet` | +| `Config\ConfigBench` | `benchConstruct`, `benchPath` | | `Container\ContainerBench` | `benchGet` | | `Db\PdoBench` | `benchFetchAll`, `benchFetchOneBound` | | `Di\DiBench` | `benchFactoryDefault`, `benchGetNew`, `benchGetShared`, `benchGetSharedMethod` | +| `Encryption\JwtBench` | `benchBuild`, `benchParse` | +| `Encryption\SecurityBench` | `benchCsrf` | | `Events\ManagerBench` | `benchFire`, `benchFireNoListener` | | `Filter\FilterBench` | `benchSanitize` | +| `Filter\ValidationBench` | `benchValidate`, `benchValidateFails` | +| `Flash\FlashBench` | `benchMessages` | +| `Forms\FormBench` | `benchConstruct`, `benchIsValid`, `benchRender` | | `Html\EscaperBench` | `benchHtml` | | `Http\ResponseBench` | `benchSend`, `benchSetJsonContent` | +| `Logger\LoggerBench` | `benchDebugSkipped`, `benchInfo` | | `Mvc\DispatcherBench` | `benchDispatch` | | `Mvc\ModelBench` | `benchFind`, `benchFindFirst`, `benchQueryJoin`, `benchSave`, `benchToArray` | | `Mvc\QueryBench` | `benchParseCold`, `benchParseWarm` | | `Mvc\RouterBench` | `benchDefineAndHandle`, `benchDefineAndHandleMethods`, `benchHandle`, `benchHandleWithEvents` | | `Mvc\ViewBench` | `benchPartial`, `benchRender` | | `Mvc\VoltBench` | `benchCompileString` | +| `Session\SessionBench` | `benchBagSet`, `benchGet`, `benchSet` | | `Support\CollectionBench` | `benchConstruct`, `benchFilter`, `benchGet`, `benchOffsetGet`, `benchRemove`, `benchSet`, `benchToJson` | | `Support\JsonBench` | `benchEncode` | @@ -116,8 +128,12 @@ One subject for each component entry point that the reference apps run (selected call. `benchRemove` sets the key and then removes it, so each call does the same work. - `benchParseCold` uses a new PHQL string for each call, so both PHQL caches miss. The caches grow during the run. -- For heavy subjects (Router, View, Model, Query, Db, `benchFactoryDefault`), use a smaller k (for example - 200) with `bin/instr`. +- The component subjects use no network: Session uses the `Noop` adapter, Cache the `memory` adapter, Logger the + bench adapter `LastLineAdapter`. +- `Forms\FormBench` sets a default container with only `escaper`: `Select::render()` uses `Phalcon\Tag`, which + reads this service. +- For heavy subjects (Router, View, Model, Query, Db, `benchFactoryDefault`, Forms, JWT, `Acl\AclBench::benchBuild`), + use a smaller k (for example 200) with `bin/instr`. ## Reference apps @@ -254,6 +270,17 @@ docker exec cphalcon-bench-8.4 tests/benchmarks/bin/run-all