Skip to content

Commit de06564

Browse files
committed
feat(Bench): Make PoC with dragon-code/benchmark (#92)
1 parent 4ed803f commit de06564

File tree

7 files changed

+77
-14
lines changed

7 files changed

+77
-14
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
],
2424
"require": {
2525
"php": ">=8.1",
26+
"dragon-code/benchmark": "^2.6 || ^3.0",
2627
"internal/destroy": "^1.0",
2728
"internal/path": "^1.2",
2829
"psr/container": "1 - 2",

src/Bench/BenchOptions.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Testo\Bench;
6+
7+
/**
8+
* Benchmarks options.
9+
*
10+
* @api
11+
*/
12+
final class BenchOptions
13+
{
14+
public function __construct(
15+
/**
16+
* @var int<1, max> Number of iterations to run for each benchmark.
17+
*/
18+
public readonly int $iterations = 1000,
19+
20+
/**
21+
* @var int<1, max> Number of revolutions to run for each benchmark.
22+
* A revolution is a single execution of the benchmarked function.
23+
*/
24+
public readonly int $revolutions = 5,
25+
) {
26+
$iterations > 0 or throw new \InvalidArgumentException('Iterations must be greater than 0.');
27+
$revolutions > 0 or throw new \InvalidArgumentException('Revolutions must be greater than 0.');
28+
}
29+
}

src/Bench/BenchWith.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
#[FallbackInterceptor(BenchWithInterceptor::class)]
1818
final class BenchWith implements Interceptable
1919
{
20-
/**
21-
* @param array<callable|array{class-string, non-empty-string}> $callables Functions to benchmark with.
22-
* It might be a callable or an array with class name and non-public method name.
23-
*/
2420
public function __construct(
21+
/**
22+
* @var array<callable|array{class-string, non-empty-string}> $callables Functions to benchmark with.
23+
* It might be a callable or an array with class name and non-public method name.
24+
*/
2525
public readonly array $callables,
2626
public readonly array $arguments = [],
27+
public readonly BenchOptions $options = new BenchOptions(),
2728
) {}
2829
}

src/Bench/Internal/BenchInvoker.php

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,46 @@
44

55
namespace Testo\Bench\Internal;
66

7+
use DragonCode\Benchmark\Benchmark;
78
use Testo\Bench\BenchWith;
89
use Testo\Bench\Exception\BenchWithAttributeMissingException;
910
use Testo\Core\Context\TestInfo;
1011

1112
final class BenchInvoker
1213
{
14+
private static function normalizeCallable(TestInfo $info, callable|array $callable): \Closure
15+
{
16+
$fn = $callable(...);
17+
return static fn (): mixed => $fn(...$info->arguments);
18+
}
19+
1320
public function __invoke(TestInfo $info): mixed
1421
{
1522
$attr = $info->getAttribute(BenchWith::class);
1623
$attr instanceof BenchWith or throw BenchWithAttributeMissingException::fromTestInfo($info);
1724

18-
# TODO: bench
19-
# Execute the method
20-
$result = $info->caseInfo->instance === null || $info->testDefinition->reflection->isStatic()
21-
? $info->testDefinition->reflection->invoke(null, ...$info->arguments)
22-
: $info->testDefinition->reflection->invoke($info->caseInfo->instance->getInstance(), ...$info->arguments);
25+
// Current function callable
26+
$fn = $info->caseInfo->instance === null || $info->testDefinition->reflection->isStatic()
27+
? $info->testDefinition->reflection->getClosure(null)
28+
: $info->testDefinition->reflection->getClosure($info->caseInfo->instance->getInstance());
29+
30+
31+
$functions = [static fn (): mixed => $fn(...$info->arguments)];
32+
33+
# Collect callables
34+
foreach ($attr->callables as $callable) {
35+
$f = self::normalizeCallable($info, $callable);
36+
$functions[] = $f;
37+
}
38+
39+
40+
Benchmark::start()
41+
->withoutData()
42+
->iterations($attr->options->iterations)
43+
->compare(
44+
...$functions,
45+
);
2346

24-
return $result;
47+
return null;
2548
}
2649
}

src/Bench/Middleware/BenchFinder.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ public function locateFile(TokenizedFile $file, callable $next): ?bool
3838
#[\Override]
3939
public function locateTestCases(FileDefinitions $file, callable $next): CaseDefinitions
4040
{
41-
echo 123;
4241
// Define cases for classes
4342
foreach ($file->classes as $class) {
4443
if ($class->isAbstract()) {

src/Pipeline/InterceptorProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Testo\Application\Middleware\Locator\TestoAttributesLocatorInterceptor;
1111
use Testo\Assert\Interceptor\AssertCollectorInterceptor;
1212
use Testo\Assert\Interceptor\ExpectationsInterceptor;
13+
use Testo\Bench\Middleware\BenchFinder;
1314
use Testo\Common\Container;
1415
use Testo\Common\Reflection;
1516
use Testo\Inline\Middleware\TestInlineFinder;
@@ -56,6 +57,7 @@ public function fromConfig(string $class): array
5657
return $this->fromClasses($class, ...[
5758
FilterInterceptor::class,
5859
TestInlineFinder::class,
60+
BenchFinder::class,
5961
new FilePostfixTestLocatorInterceptor(),
6062
new TestoAttributesLocatorInterceptor(),
6163
new AssertCollectorInterceptor(),

tests/Bench/Self/BenchWithAttr.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@
44

55
namespace Tests\Bench\Self;
66

7+
use Testo\Bench\BenchOptions;
78
use Testo\Bench\BenchWith;
89

910
final class BenchWithAttr
1011
{
11-
#[BenchWith([
12-
[self::class, 'sumSlow'],
13-
], arguments: [1, 2])]
12+
#[BenchWith(
13+
[
14+
[self::class, 'sumFast'],
15+
[self::class, 'sumSlow'],
16+
],
17+
arguments: [1, 2],
18+
options: new BenchOptions(
19+
iterations: 10_000,
20+
)
21+
)]
1422
public static function sumFast(int $a, int $b): int
1523
{
1624
return $a + $b;

0 commit comments

Comments
 (0)