1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
<?php
declare(strict_types=1);
/**
* Miniroute benchmark — registration and dispatch scaling.
*
* Ported from the tmp-miniroute stress test (section 5) so the performance
* characteristics stay measurable as the kernel evolves. Run manually:
*
* composer bench # or: php tests/bench/bench.php
*
* Timings are intentionally NOT PHPUnit assertions — wall-clock numbers are
* too flaky for CI. The v1.0.0 release (miniroute-18h.3) adds hard CI gates
* (register < 5ms for the full table, dispatch < 0.1ms).
*
* Expected shape: registration is super-linear in v0.1.0 (usort per add,
* ~770ms at 600 routes) and collapses once miniroute-uhs.5 (lazy sort)
* lands. Dispatch scales linearly in route count.
*/
require dirname(__DIR__) . '/bootstrap.php';
use BrettParson\MiniRoute\Attribute\Get;
use BrettParson\MiniRoute\Http\RequestInterface;
use BrettParson\MiniRoute\Http\ResponseInterface;
use BrettParson\MiniRoute\Routing\RouteNotFoundException;
use BrettParson\MiniRoute\Routing\Router;
use BrettParson\MiniRoute\Tests\Fixtures\AdminLoggingMiddleware;
use BrettParson\MiniRoute\Tests\Fixtures\CsrfLoggingMiddleware;
use BrettParson\MiniRoute\Tests\Fixtures\FakeResolver;
use BrettParson\MiniRoute\Tests\Fixtures\RealisticAdminController;
use BrettParson\MiniRoute\Tests\Fixtures\RealisticPublicController;
use BrettParson\MiniRoute\Tests\Fixtures\TestRequest;
use BrettParson\MiniRoute\Tests\Fixtures\TestResponse;
/** @return ResponseInterface|null body, or null on no-match */
function benchHit(Router $router, string $method, string $path): ?string
{
try {
return $router->dispatch(new TestRequest($method, $path))->body();
} catch (RouteNotFoundException) {
return null;
}
}
function benchMs(float $start): float
{
return (microtime(true) - $start) * 1000;
}
echo "miniroute benchmark\n";
echo str_repeat('-', 64) . "\n";
// ── Realistic table: registration + 2000 dispatches ────────────────────────
$resolver = new FakeResolver();
$resolver->bind(RealisticPublicController::class, new RealisticPublicController());
$resolver->bind(RealisticAdminController::class, new RealisticAdminController());
$resolver->bind(AdminLoggingMiddleware::class, new AdminLoggingMiddleware(new ArrayObject()));
$resolver->bind(CsrfLoggingMiddleware::class, new CsrfLoggingMiddleware(new ArrayObject()));
$real = new Router($resolver);
$real->group('/admin', [AdminLoggingMiddleware::class]);
$real->group('/admin', [CsrfLoggingMiddleware::class], ['POST']);
$start = microtime(true);
$real->registerController(RealisticPublicController::class);
$real->registerController(RealisticAdminController::class);
$regMs = benchMs($start);
$paths = ['/entries', '/entries/hello-world', '/notes/42', '/admin', '/admin/notes', '/admin/entries/preview', '/feed.xml', '/robots.txt', '/sitemap.xml'];
$start = microtime(true);
for ($i = 0; $i < 2000; $i++) {
benchHit($real, 'GET', $paths[$i % count($paths)]);
}
$dispatchMs = benchMs($start);
printf("realistic (22 routes): register = %8.3fms | 2000 dispatches = %8.2fms (%0.5fms each)\n", $regMs, $dispatchMs, $dispatchMs / 2000);
// ── Scaling: synthetic routes at 100 / 300 / 600 ───────────────────────────
printf("\n%-22s %-20s %s\n", 'routes', 'register', 'leaf dispatch (each)');
foreach ([100, 300, 600] as $n) {
$resolver = new FakeResolver();
$bench = new Router($resolver);
$start = microtime(true);
for ($i = 0; $i < $n; $i++) {
$class = 'BenchRoute' . $n . '_' . $i;
eval('final class ' . $class . ' { #[' . Get::class . '("/bench/' . $n . '/' . $i . '")] public function run(): ' . ResponseInterface::class . ' { return new ' . TestResponse::class . '(200, "b"); } }');
$resolver->bind($class, new $class());
$bench->registerController($class);
}
$regMs = benchMs($start);
$start = microtime(true);
for ($i = 0; $i < 200; $i++) {
benchHit($bench, 'GET', '/bench/' . $n . '/' . ($n - 1));
}
$dispatchMs = benchMs($start) / 200;
printf("%-22d %8.1fms %s %0.5fms\n", $n, $regMs, str_repeat(' ', 11), $dispatchMs);
}
echo str_repeat('-', 64) . "\n";
|