From f8575224a07318fbae32c348c5419d3c24910273 Mon Sep 17 00:00:00 2001 From: Viktor Babanov Date: Sun, 13 Oct 2024 19:42:34 +0500 Subject: [PATCH 1/3] First benchmarks (#219) * First benchmarks * Apply fixes from StyleCI * remove debug * Add benchmark CI step --------- Co-authored-by: StyleCI Bot --- .github/workflows/bechmark.yml | 33 +++++++ composer.json | 1 + phpbench.json | 8 ++ tests/Benchmark/MetadataBench.php | 109 +++++++++++++++++++++++ tests/Benchmark/QueueBench.php | 112 ++++++++++++++++++++++++ tests/Benchmark/Support/VoidAdapter.php | 52 +++++++++++ tests/docker/php/Dockerfile | 12 +-- tests/docker/php/php.ini | 2 + 8 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/bechmark.yml create mode 100644 phpbench.json create mode 100644 tests/Benchmark/MetadataBench.php create mode 100644 tests/Benchmark/QueueBench.php create mode 100644 tests/Benchmark/Support/VoidAdapter.php create mode 100644 tests/docker/php/php.ini diff --git a/.github/workflows/bechmark.yml b/.github/workflows/bechmark.yml new file mode 100644 index 00000000..36190113 --- /dev/null +++ b/.github/workflows/bechmark.yml @@ -0,0 +1,33 @@ +on: + pull_request: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'infection.json.dist' + - 'psalm.xml' + - 'tests/**' + + push: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'infection.json.dist' + - 'psalm.xml' + - 'tests/**' + +name: bechmark + +jobs: + phpbench: + uses: yiisoft/actions/.github/workflows/phpbench.yml@master + with: + os: >- + ['ubuntu-latest', 'windows-latest'] + php: >- + ['8.1'] diff --git a/composer.json b/composer.json index c59f8ca9..664f5853 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,7 @@ }, "require-dev": { "maglnet/composer-require-checker": "^4.7", + "phpbench/phpbench": "^1.3", "phpunit/phpunit": "^10.5", "rector/rector": "^1.0.0", "roave/infection-static-analysis-plugin": "^1.34", diff --git a/phpbench.json b/phpbench.json new file mode 100644 index 00000000..04436028 --- /dev/null +++ b/phpbench.json @@ -0,0 +1,8 @@ +{ + "$schema":"./vendor/phpbench/phpbench/phpbench.schema.json", + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "tests/Benchmark", + "runner.revs": 100000, + "runner.iterations": 5, + "runner.warmup": 5 +} diff --git a/tests/Benchmark/MetadataBench.php b/tests/Benchmark/MetadataBench.php new file mode 100644 index 00000000..459cd53b --- /dev/null +++ b/tests/Benchmark/MetadataBench.php @@ -0,0 +1,109 @@ + 1]); + $id = $message->getMetadata()['id']; + } + + /** + * Create metadata as object and read its value immediately + */ + #[Tag('metadata_read')] + public function benchEnvelopeRead(): void + { + $message = new IdEnvelope(new Message('foo', 'bar'), 1); + $id = $message->getId(); + } + + /** + * Create metadata as array and read its value from an envelope object + */ + #[Tag('metadata_read')] + public function benchEnvelopeReadRestored(): void + { + $message = IdEnvelope::fromMessage(new Message('foo', 'bar', ['id' => 1])); + $id = $message->getId(); + } + + public function provideEnvelopeStack(): Generator + { + $config = [1 => 'one', 5 => 'three', 15 => 'fifteen']; + $message = new IdEnvelope(new Message('foo', 'bar'), 1); + + for ($i = 1; $i <= max(...array_keys($config)); $i++) { + $message = new FailureEnvelope($message, ["fail$i" => "fail$i"]); + if (isset($config[$i])) { + yield $config[$i] => ['message' => $message]; + } + } + } + + /** + * Read metadata value from an envelope object restored from an envelope stacks of different depth + * + * @psalm-param array{message: MessageInterface} $params + */ + #[ParamProviders('provideEnvelopeStack')] + #[Tag('metadata_read')] + public function benchEnvelopeReadFromStack(array $params): void + { + $id = IdEnvelope::fromMessage($params['message'])->getId(); + } + + public function provideEnvelopeStackCounts(): Generator + { + yield 'one' => [1]; + yield 'three' => [3]; + yield 'fifteen' => [15]; + } + + /** + * Create envelope stack with the given depth + * + * @psalm-param array{0: int} $params + */ + #[ParamProviders('provideEnvelopeStackCounts')] + #[Tag('metadata_create')] + public function benchEnvelopeStackCreation(array $params): void + { + $message = new Message('foo', 'bar'); + for ($i = 0; $i < $params[0]; $i++) { + $message = new FailureEnvelope($message, ["fail$i" => "fail$i"]); + } + } + + /** + * Create metadata array with the given elements count + * + * @psalm-param array{0: int} $params + */ + #[ParamProviders('provideEnvelopeStackCounts')] + #[Tag('metadata_create')] + public function benchMetadataArrayCreation(array $params): void + { + $metadata = ['failure-meta' => []]; + for ($i = 0; $i < $params[0]; $i++) { + $metadata['failure-meta']["fail$i"] = "fail$i"; + } + $message = new Message('foo', 'bar', $metadata); + } +} diff --git a/tests/Benchmark/QueueBench.php b/tests/Benchmark/QueueBench.php new file mode 100644 index 00000000..76480523 --- /dev/null +++ b/tests/Benchmark/QueueBench.php @@ -0,0 +1,112 @@ + static function (): void { + }, + ], + $logger, + new Injector($container), + $container, + new ConsumeMiddlewareDispatcher(new MiddlewareFactoryConsume($container, $callableFactory)), + new FailureMiddlewareDispatcher( + new MiddlewareFactoryFailure($container, $callableFactory), + [], + ), + ); + $this->serializer = new JsonMessageSerializer(); + $this->adapter = new VoidAdapter($this->serializer); + + $this->queue = new Queue( + $worker, + new SimpleLoop(0), + $logger, + new PushMiddlewareDispatcher(new MiddlewareFactoryPush($container, $callableFactory)), + $this->adapter, + ); + } + + public function providePush(): Generator + { + yield 'simple' => ['message' => new Message('foo', 'bar')]; + yield 'with envelopes' => [ + 'message' => new FailureEnvelope( + new IdEnvelope( + new Message('foo', 'bar'), + 'test id', + ), + ['failure-1' => ['a', 'b', 'c']], + ), + ]; + } + + #[ParamProviders('providePush')] + #[Tag('queue_push')] + public function benchPush(array $params): void + { + $this->queue->push($params['message']); + } + + public function provideConsume(): Generator + { + yield 'simple mapping' => ['message' => $this->serializer->serialize(new Message('foo', 'bar'))]; + yield 'with envelopes mapping' => [ + 'message' => $this->serializer->serialize( + new FailureEnvelope( + new IdEnvelope( + new Message('foo', 'bar'), + 'test id', + ), + ['failure-1' => ['a', 'b', 'c']], + ), + ), + ]; + } + + #[ParamProviders('provideConsume')] + #[Tag('queue_consume')] + public function benchConsume(array $params): void + { + $this->adapter->message = $params['message']; + $this->queue->run(); + } +} diff --git a/tests/Benchmark/Support/VoidAdapter.php b/tests/Benchmark/Support/VoidAdapter.php new file mode 100644 index 00000000..edd927aa --- /dev/null +++ b/tests/Benchmark/Support/VoidAdapter.php @@ -0,0 +1,52 @@ +serializer->unserialize($this->message)); + } + + public function status(int|string $id): JobStatus + { + throw new InvalidArgumentException(); + } + + public function push(MessageInterface $message): MessageInterface + { + $this->serializer->serialize($message); + + return new IdEnvelope($message, 1); + } + + public function subscribe(callable $handlerCallback): void + { + throw new RuntimeException('Method is not implemented'); + } + + public function withChannel(string $channel): AdapterInterface + { + throw new RuntimeException('Method is not implemented'); + } +} diff --git a/tests/docker/php/Dockerfile b/tests/docker/php/Dockerfile index a2051c02..cd3e6270 100644 --- a/tests/docker/php/Dockerfile +++ b/tests/docker/php/Dockerfile @@ -1,17 +1,19 @@ # Important! Do not use this image in production! ARG PHP_VERSION -FROM --platform=linux/amd64 php:${PHP_VERSION}-cli-alpine +FROM php:${PHP_VERSION}-cli-alpine -RUN apk add git autoconf g++ make linux-headers +RUN apk add git autoconf g++ make linux-headers && \ + docker-php-ext-install pcntl && \ + pecl install xdebug pcov && \ + docker-php-ext-enable xdebug pcov -RUN docker-php-ext-install pcntl -RUN pecl install xdebug pcov -RUN docker-php-ext-enable xdebug pcov +ADD ./tests/docker/php/php.ini /usr/local/etc/php/conf.d/40-custom.ini COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 WORKDIR /app +RUN git config --global --add safe.directory /app ENTRYPOINT ["sh", "tests/docker/php/entrypoint.sh"] CMD ["sleep", "infinity"] diff --git a/tests/docker/php/php.ini b/tests/docker/php/php.ini new file mode 100644 index 00000000..18fdd3e5 --- /dev/null +++ b/tests/docker/php/php.ini @@ -0,0 +1,2 @@ +opcache.enable=1 +opcache.enable_cli=1 From 65cf57112ca5f932ec569fbc13ca9f04cd82254a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 30 Oct 2024 16:20:37 +0300 Subject: [PATCH 2/3] Remove scrutinizer (#221) --- .gitattributes | 1 - .scrutinizer.yml | 35 ----------------------------------- README.md | 3 +-- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 .scrutinizer.yml diff --git a/.gitattributes b/.gitattributes index 862e7288..7382fb1b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -26,7 +26,6 @@ /.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/.scrutinizer.yml export-ignore /.dockerignore export-ignore /.phpunit-watcher.yml export-ignore /.styleci.yml export-ignore diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 2b695c2e..00000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,35 +0,0 @@ -checks: - php: true - -filter: - paths: - - src/* - -build: - image: default-bionic - - environment: - php: - version: 8.1.26 - ini: - xdebug.mode: coverage - - nodes: - analysis: - tests: - override: - - php-scrutinizer-run - - phpunit: - dependencies: - override: - - composer self-update - - composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - tests: - override: - - command: ./vendor/bin/phpunit --coverage-clover ./coverage.xml - on_node: 1 - coverage: - file: coverage.xml - format: php-clover diff --git a/README.md b/README.md index ed467fab..25f94f74 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ [![Latest Stable Version](https://poser.pugx.org/yiisoft/queue/v/stable.svg)](https://packagist.org/packages/yiisoft/queue) [![Total Downloads](https://poser.pugx.org/yiisoft/queue/downloads.svg)](https://packagist.org/packages/yiisoft/queue) [![Build status](https://github.com/yiisoft/queue/workflows/build/badge.svg)](https://github.com/yiisoft/queue/actions) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/queue/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/queue/?branch=master) -[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/queue/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/queue/?branch=master) +[![Code coverage](https://codecov.io/gh/yiisoft/queue/graph/badge.svg?token=NU2ST01B1U)](https://codecov.io/gh/yiisoft/queue) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Fqueue%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/queue/master) [![static analysis](https://github.com/yiisoft/queue/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/queue/actions?query=workflow%3A%22static+analysis%22) [![type-coverage](https://shepherd.dev/github/yiisoft/queue/coverage.svg)](https://shepherd.dev/github/yiisoft/queue) From 2bd3388f470610c09a66f6791730b2e27eb7a4b1 Mon Sep 17 00:00:00 2001 From: viktorprogger Date: Sun, 10 Nov 2024 23:18:00 +0500 Subject: [PATCH 3/3] Update benchmark workflow --- .github/workflows/bechmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bechmark.yml b/.github/workflows/bechmark.yml index 36190113..2751bdf5 100644 --- a/.github/workflows/bechmark.yml +++ b/.github/workflows/bechmark.yml @@ -30,4 +30,4 @@ jobs: os: >- ['ubuntu-latest', 'windows-latest'] php: >- - ['8.1'] + ['8.1', '8.2', '8.3']