perf(compiler): group functions for declaration header generation (#129)
parent
69e2a29ec2
commit
63a4793e0c
6 changed files with 190 additions and 16 deletions
@ -0,0 +1,55 @@ |
|||||||
|
# Declaration header generation benchmark |
||||||
|
|
||||||
|
Creates 300 PHP source files with 6,001 functions, each ordinary function |
||||||
|
having a default parameter. It prepares and converts the project, then measures |
||||||
|
six calls to the real declaration-header generator and discards the first as |
||||||
|
warm-up. The timed region includes rendering and writing headers; source |
||||||
|
preparation, conversion, and C++ compilation are excluded. This measures one |
||||||
|
compiler phase, not total build time. |
||||||
|
|
||||||
|
Run from a checkout with Composer development dependencies installed: |
||||||
|
|
||||||
|
```sh |
||||||
|
project_dir=$(mktemp -d) |
||||||
|
php benchmark/declaration-headers/run.php "$project_dir" > /tmp/declaration-candidate.log |
||||||
|
tail -1 /tmp/declaration-candidate.log |
||||||
|
``` |
||||||
|
|
||||||
|
The script writes generated sources and build outputs into the supplied |
||||||
|
benchmark directory. Reuse the same directory for comparisons: absolute source |
||||||
|
paths contribute to header names and therefore the combined content digest. |
||||||
|
|
||||||
|
The baseline does not contain this new benchmark. From the candidate checkout, |
||||||
|
these commands create a separate baseline and run the same source in both. The |
||||||
|
shared vendor directory is supported by `phpunit/bootstrap.php`'s checkout-local |
||||||
|
source loader. |
||||||
|
|
||||||
|
```sh |
||||||
|
baseline_dir=$(mktemp -d) |
||||||
|
project_dir=$(mktemp -d) |
||||||
|
git archive b3898c32 | tar -x -C "$baseline_dir" |
||||||
|
ln -s "$(pwd)/vendor" "$baseline_dir/vendor" |
||||||
|
mkdir -p "$baseline_dir/benchmark/declaration-headers" |
||||||
|
cp benchmark/declaration-headers/run.php "$baseline_dir/benchmark/declaration-headers/run.php" |
||||||
|
php "$baseline_dir/benchmark/declaration-headers/run.php" "$project_dir" > /tmp/declaration-baseline.log |
||||||
|
php benchmark/declaration-headers/run.php "$project_dir" > /tmp/declaration-candidate.log |
||||||
|
tail -1 /tmp/declaration-baseline.log |
||||||
|
tail -1 /tmp/declaration-candidate.log |
||||||
|
``` |
||||||
|
|
||||||
|
Compare median `ms` values and require identical `headers` counts and `sha256` |
||||||
|
digests. The digest covers the contents and names of every generated declaration |
||||||
|
header, including runtime and aggregate headers. |
||||||
|
|
||||||
|
## Sample result |
||||||
|
|
||||||
|
Linux ARM64 Docker, PHP 8.5.10 ZTS, CLI opcache disabled; baseline `b3898c32`: |
||||||
|
|
||||||
|
| Revision | Median (ms) | Headers | |
||||||
|
| --- | ---: | ---: | |
||||||
|
| Baseline | 88.001 | 302 | |
||||||
|
| Grouped functions | 13.087 | 302 | |
||||||
|
|
||||||
|
The measured phase is about 6.72x faster, with identical output digests. Raw |
||||||
|
samples are in `results-arm64.json`. This workload isolates function-heavy |
||||||
|
projects; class and constant declaration scans are not optimized by this change. |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
{ |
||||||
|
"environment": "Linux ARM64 Docker, PHP 8.5.10 ZTS, no CLI opcache", |
||||||
|
"baseline_commit": "b3898c32", |
||||||
|
"files": 300, |
||||||
|
"functions": 6001, |
||||||
|
"baseline": { |
||||||
|
"ms": [ |
||||||
|
88.000792, |
||||||
|
87.998334, |
||||||
|
90.341888, |
||||||
|
90.412138, |
||||||
|
87.537832 |
||||||
|
], |
||||||
|
"headers": 302, |
||||||
|
"sha256": "bfad6f04ca81c591734aeeaf223cc32ef7064768ca12faba8ac41ef053444f37", |
||||||
|
"median_ms": 88.000792 |
||||||
|
}, |
||||||
|
"candidate": { |
||||||
|
"ms": [ |
||||||
|
13.015777, |
||||||
|
13.490487, |
||||||
|
12.763525, |
||||||
|
13.087318, |
||||||
|
13.106777 |
||||||
|
], |
||||||
|
"headers": 302, |
||||||
|
"sha256": "bfad6f04ca81c591734aeeaf223cc32ef7064768ca12faba8ac41ef053444f37", |
||||||
|
"median_ms": 13.087318 |
||||||
|
}, |
||||||
|
"speedup": 6.7241272810823425 |
||||||
|
} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
<?php |
||||||
|
require dirname(__DIR__, 2) . '/phpunit/bootstrap.php'; |
||||||
|
if ($argc !== 2 || !is_dir($argv[1])) { |
||||||
|
fwrite(STDERR, "Usage: php run.php <existing temporary project directory>\n"); |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
$root = realpath($argv[1]); |
||||||
|
$files = []; |
||||||
|
for ($i = 0; $i < 300; ++$i) { |
||||||
|
$source = "<?php\n";
|
||||||
|
for ($j = 0; $j < 20; ++$j) { |
||||||
|
$source .= "function f_{$i}_{$j}(int \$value = 1): int { return \$value; }\n"; |
||||||
|
} |
||||||
|
if ($i === 0) { |
||||||
|
$source .= "function main(): void {}\n"; |
||||||
|
} |
||||||
|
$file = "$root/file_$i.php"; |
||||||
|
file_put_contents($file, $source); |
||||||
|
$files[] = $file; |
||||||
|
} |
||||||
|
$translator = $compiler = TypePhp\CompilerTest::create($root); |
||||||
|
$compiler->setTargetName('declaration_benchmark'); |
||||||
|
(new ReflectionMethod($compiler, 'setBuildDir'))->invoke($compiler, "$root/build"); |
||||||
|
$compiler->addFiles($files); |
||||||
|
foreach ($files as $file) { |
||||||
|
$compiler->prepareFile($file); |
||||||
|
} |
||||||
|
$compiler->convert($files); |
||||||
|
$generate = new ReflectionMethod($compiler, 'genDeclarationHeaders'); |
||||||
|
$samples = []; |
||||||
|
for ($i = 0; $i < 6; ++$i) { |
||||||
|
$start = hrtime(true); |
||||||
|
$generate->invoke($compiler, $files); |
||||||
|
if ($i > 0) { |
||||||
|
$samples[] = (hrtime(true) - $start) / 1e6; |
||||||
|
} |
||||||
|
} |
||||||
|
$hashes = []; |
||||||
|
foreach (glob("$root/build/include/*decl.h") as $file) { |
||||||
|
$hashes[basename($file)] = hash_file('sha256', $file); |
||||||
|
} |
||||||
|
ksort($hashes); |
||||||
|
echo "BENCHMARK_RESULT=" . json_encode(['ms' => $samples, 'headers' => count($hashes), 'sha256' => hash('sha256', json_encode($hashes))]) . "\n"; |
||||||
Loading…
Reference in new issue