Skip to content

Commit 1a92e78

Browse files
authored
[bench] Add FileInfoMatcher::matchPattern realpath benchmark (#8703)
1 parent 7aa3326 commit 1a92e78

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

‎bench/file_info_matcher_bench.php‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Benchmark for FileInfoMatcher::matchPattern().
7+
*
8+
* A skip pattern that is not a literal prefix/suffix and does not fnmatch falls through to
9+
* matchRealpath(), which calls realpath() on the pattern. PHP does not cache a *failed*
10+
* realpath() lookup, so a wildcard pattern (realpath = false) pays a filesystem syscall for
11+
* every single file checked. On a large run that is one failed realpath() per file per pattern.
12+
*
13+
* This bench drives the real matchPattern() over many real files with a set of such patterns,
14+
* so every call reaches the realpath() lookup. The patterns never match, so matchPattern()
15+
* returns null and does no other work, making the calls repeatable.
16+
*
17+
* Run on both branches to compare:
18+
* php bench/file_info_matcher_bench.php
19+
*
20+
* Reports CPU time (user+sys) and wall time; CPU is the reliable metric, wall is noisy
21+
* under concurrent load.
22+
*/
23+
24+
use Rector\Skipper\Matcher\FileInfoMatcher;
25+
26+
require __DIR__ . '/../vendor/autoload.php';
27+
28+
const REPEAT = 40; // outer repeats over the whole file set
29+
30+
$fileInfoMatcher = new FileInfoMatcher();
31+
32+
// many real files, so each pattern's realpath() lookup is repeated per file within a run
33+
$filePaths = array_values(array_filter(
34+
glob(__DIR__ . '/../src/*/*.php') ?: [],
35+
static fn (string $filePath): bool => is_file($filePath)
36+
));
37+
38+
// wildcard patterns whose realpath() is always false (nonexistent dirs) - the uncached hot case
39+
$filePatterns = [
40+
'*/__no_such_dir_a__/*',
41+
'*/__no_such_dir_b__/*',
42+
'*/__no_such_dir_c__/*',
43+
];
44+
45+
$cpuSeconds = static function (): float {
46+
$usage = getrusage();
47+
return $usage['ru_utime.tv_sec'] + $usage['ru_utime.tv_usec'] / 1_000_000
48+
+ $usage['ru_stime.tv_sec'] + $usage['ru_stime.tv_usec'] / 1_000_000;
49+
};
50+
51+
$run = static function () use ($fileInfoMatcher, $filePaths, $filePatterns): void {
52+
foreach ($filePaths as $filePath) {
53+
$fileInfoMatcher->matchPattern($filePath, $filePatterns);
54+
}
55+
};
56+
57+
$measure = static function () use ($run, $cpuSeconds): array {
58+
// warm up autoloading once, excluded from timing
59+
$run();
60+
61+
$cpuStart = $cpuSeconds();
62+
$wallStart = hrtime(true);
63+
64+
for ($repeat = 0; $repeat < REPEAT; ++$repeat) {
65+
$run();
66+
}
67+
68+
return [
69+
'cpu' => $cpuSeconds() - $cpuStart,
70+
'wall' => (hrtime(true) - $wallStart) / 1_000_000_000,
71+
];
72+
};
73+
74+
$result = $measure();
75+
76+
printf('FileInfoMatcher::matchPattern benchmark%s', PHP_EOL);
77+
printf(' files=%d patterns=%d repeat=%d%s', count($filePaths), count($filePatterns), REPEAT, PHP_EOL);
78+
printf('%s', PHP_EOL);
79+
printf(' matchPattern cpu=%.4fs wall=%.4fs%s', $result['cpu'], $result['wall'], PHP_EOL);

0 commit comments

Comments
 (0)