Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
/.github/workflows/trigger-tempest-app-qa.yml @brendt
/.github/workflows/trigger-tempest-clean-qa.yml @brendt
/.github/workflows/validate-packages.yml @aidan-casey
/.github/workflows/benchmark.yml @xHeaven
/.github/workflows/benchmark-comment.yml @xHeaven
/src/ @brendt
/docs/ @brendt @innocenzi
/packages/auth/ @innocenzi
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/benchmark-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Benchmark Comment

on:
workflow_run:
workflows: [Benchmark]
types: [completed]

jobs:
comment:
name: Post Benchmark Results
runs-on: ubuntu-latest
permissions:
actions: read
pull-requests: write
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'

steps:
- name: Download benchmark artifact
uses: actions/download-artifact@v7
with:
name: benchmark-result
path: benchmark-artifact/
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}

- name: Read artifact data
id: bench
run: |
echo "pr-number=$(cat benchmark-artifact/pr-number.txt)" >> "$GITHUB_OUTPUT"
echo "head-ref=$(cat benchmark-artifact/head-ref.txt)" >> "$GITHUB_OUTPUT"
echo "base-ref=$(cat benchmark-artifact/base-ref.txt)" >> "$GITHUB_OUTPUT"
echo "base-sha=$(cat benchmark-artifact/base-sha.txt)" >> "$GITHUB_OUTPUT"
echo "head-sha=$(cat benchmark-artifact/head-sha.txt)" >> "$GITHUB_OUTPUT"
{
echo "result<<BENCH_EOF"
cat benchmark-artifact/benchmark-result.txt
echo "BENCH_EOF"
} >> "$GITHUB_OUTPUT"

- name: Find existing comment
uses: peter-evans/find-comment@v4
id: find-comment
with:
issue-number: ${{ steps.bench.outputs.pr-number }}
comment-author: github-actions[bot]
body-includes: "## Benchmark Results"

- name: Post benchmark results
uses: peter-evans/create-or-update-comment@v5
with:
issue-number: ${{ steps.bench.outputs.pr-number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body: |
## Benchmark Results

Comparison of `${{ steps.bench.outputs.head-ref }}` against `${{ steps.bench.outputs.base-ref }}` (`${{ steps.bench.outputs.base-sha }}`).

<details>
<summary>Open to see the benchmark results</summary>

${{ steps.bench.outputs.result }}

</details>

<sub>Generated by phpbench against commit ${{ steps.bench.outputs.head-sha }}</sub>
59 changes: 59 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Benchmark

on:
pull_request:
branches: [main]

concurrency:
group: benchmark-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
benchmark:
name: Performance Regression Check
runs-on: ubuntu-latest

steps:
- name: Checkout PR branch
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.4
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
coverage: none

- name: Install dependencies
uses: ramsey/composer-install@v3

- name: Benchmark base branch
run: |
git checkout ${{ github.event.pull_request.base.sha }}
composer install --no-interaction --prefer-dist --quiet
vendor/bin/phpbench run --tag=base --store --progress=none

- name: Benchmark PR branch
run: |
git checkout ${{ github.event.pull_request.head.sha }}
composer install --no-interaction --prefer-dist --quiet
vendor/bin/phpbench run --tag=pr --store --ref=base --report=aggregate --progress=none --output=markdown | tee benchmark-result.txt || true

- name: Prepare artifact
run: |
mkdir -p benchmark-artifact
cp benchmark-result.txt benchmark-artifact/
echo "${{ github.event.pull_request.number }}" > benchmark-artifact/pr-number.txt
echo "${{ github.head_ref }}" > benchmark-artifact/head-ref.txt
echo "${{ github.base_ref }}" > benchmark-artifact/base-ref.txt
echo "${{ github.event.pull_request.base.sha }}" > benchmark-artifact/base-sha.txt
echo "${{ github.event.pull_request.head.sha }}" > benchmark-artifact/head-sha.txt

- name: Upload benchmark artifact
uses: actions/upload-artifact@v6
with:
name: benchmark-result
path: benchmark-artifact/
retention-days: 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.tempest/
.cache/
.idea/
.phpbench/
build/
sessions/
vendor/
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"nyholm/psr7": "^1.8",
"patrickbussmann/oauth2-apple": "^0.3",
"phpat/phpat": "^0.11.0",
"phpbench/phpbench": "84.x-dev",
"phpbench/phpbench": "^1.4",
"phpstan/phpstan": "2.1.38",
"phpunit/phpunit": "^12.5.8",
"predis/predis": "^3.0.0",
Expand Down Expand Up @@ -274,6 +274,7 @@
"composer phpunit",
"composer phpstan",
"composer exceptions:build"
]
],
"bench": "vendor/bin/phpbench run --report=aggregate"
}
}
9 changes: 7 additions & 2 deletions phpbench.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
"runner.bootstrap": "vendor/autoload.php"
"$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "tests/Benchmark",
"runner.file_pattern": "*Bench.php",
"core.extensions": [
"Tests\\Tempest\\Benchmark\\Extension\\MarkdownExtension"
]
}
114 changes: 114 additions & 0 deletions tests/Benchmark/Container/ContainerBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Tests\Tempest\Benchmark\Container;

use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use PhpBench\Attributes\Warmup;
use Tempest\Container\GenericContainer;
use Tempest\Container\Tests\Fixtures\ClassWithSingletonAttribute;
use Tempest\Container\Tests\Fixtures\ContainerObjectA;
use Tempest\Container\Tests\Fixtures\ContainerObjectB;
use Tempest\Container\Tests\Fixtures\ContainerObjectD;
use Tempest\Container\Tests\Fixtures\ContainerObjectDInitializer;
use Tempest\Container\Tests\Fixtures\ContainerObjectE;
use Tempest\Container\Tests\Fixtures\ContainerObjectEInitializer;

final class ContainerBench
{
private GenericContainer $container;

public function setUp(): void
{
$this->container = new GenericContainer();
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchAutowireSimple(): void
{
$this->container->get(ContainerObjectA::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchAutowireNested(): void
{
$this->container->get(ContainerObjectB::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchSingletonResolution(): void
{
$this->container->singleton(ContainerObjectA::class, new ContainerObjectA());
$this->container->get(ContainerObjectA::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchSingletonAttribute(): void
{
$this->container->get(ClassWithSingletonAttribute::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchDefinitionResolution(): void
{
$this->container->register(ContainerObjectA::class, fn () => new ContainerObjectA());
$this->container->get(ContainerObjectA::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchInitializerResolution(): void
{
$this->container->addInitializer(ContainerObjectDInitializer::class);
$this->container->get(ContainerObjectD::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchDynamicInitializerResolution(): void
{
$this->container->addInitializer(ContainerObjectEInitializer::class);
$this->container->get(ContainerObjectE::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchClosureSingletonResolution(): void
{
$this->container->singleton(ContainerObjectA::class, fn () => new ContainerObjectA());
$this->container->get(ContainerObjectA::class);
}

#[BeforeMethods('setUp')]
#[Iterations(5)]
#[Revs(1000)]
#[Warmup(10)]
public function benchInvokeClosure(): void
{
$this->container->invoke(fn (ContainerObjectA $a) => $a);
}
}
37 changes: 37 additions & 0 deletions tests/Benchmark/Extension/MarkdownExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Tests\Tempest\Benchmark\Extension;

use PhpBench\DependencyInjection\Container;
use PhpBench\DependencyInjection\ExtensionInterface;
use PhpBench\Extension\ConsoleExtension;
use PhpBench\Extension\ExpressionExtension;
use PhpBench\Extension\ReportExtension;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class MarkdownExtension implements ExtensionInterface
{
public function configure(OptionsResolver $resolver): void
{
}

public function load(Container $container): void
{
$container->register(
MarkdownRenderer::class,
function (Container $container) {
return new MarkdownRenderer(
$container->get(ConsoleExtension::SERVICE_OUTPUT_STD),
$container->get(ExpressionExtension::SERVICE_PLAIN_PRINTER),
);
},
[
ReportExtension::TAG_REPORT_RENDERER => [
'name' => 'markdown',
],
],
);
}
}
Loading