perf(compiler): bypass dynamic dispatch for known-array appends (#97)
parent
12b0781914
commit
d1136cfca0
9 changed files with 173 additions and 0 deletions
@ -0,0 +1,35 @@ |
||||
# Known-array append benchmark |
||||
|
||||
This benchmark appends a function result to a known array, 1,000 times per |
||||
round, and accumulates a checksum. It isolates the append fallback that must |
||||
materialize its RHS before writing. It does not represent every PHP workload. |
||||
|
||||
With PHP 8.5 and a matching PHPX/embed installation, from this directory: |
||||
|
||||
```sh |
||||
php run.php 100000 |
||||
php ../../bin/tpc.php project.yml --no-progress -o array_append_benchmark |
||||
./array_append_benchmark 100000 |
||||
``` |
||||
|
||||
The checksum must be `200000000`. Build baseline and candidate revisions into |
||||
separate build directories and binary paths. Keep PHPX, PHP, compiler flags, |
||||
and machine fixed. Alternate binary execution order, discard a warm-up pair, |
||||
and compare medians across multiple runs; exclude compilation time. |
||||
|
||||
## Sample result |
||||
|
||||
Linux ARM64 in Docker, PHP 8.5.10 ZTS, PHPX `6a68f38`, GCC `-O2`, no LTO; |
||||
baseline TypePHP `692841a6` versus direct known-array append lowering. |
||||
Nine measured runs per binary after one discarded pair, 100,000 rounds: |
||||
|
||||
| Build | Median | Range | |
||||
| --- | ---: | ---: | |
||||
| Baseline | 980.88 ms | 970.47–1018.14 ms | |
||||
| Direct append | 563.60 ms | 558.99–582.68 ms | |
||||
|
||||
Elapsed time decreased by 42.54% for this append-heavy microbenchmark; all |
||||
nine paired runs were faster. This is not an application-wide speedup or a |
||||
comparison against native PHP. Measurements include process startup and were |
||||
collected on a shared development machine. Raw times in seconds are in |
||||
`results-arm64.json`; its `count` field is the number of rounds. |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
function makeAppendValue(int $value): int { return $value + 1; } |
||||
function main(int $argc, array $argv): void |
||||
{ |
||||
$rounds = $argc > 1 ? (int) $argv[1] : 10000; |
||||
$checksum = 0; |
||||
for ($r = 0; $r < $rounds; ++$r) { |
||||
$items = []; |
||||
for ($i = 0; $i < 1000; ++$i) { |
||||
$items[] = makeAppendValue($i); |
||||
} |
||||
$checksum += count($items) + $items[999]; |
||||
} |
||||
echo $checksum, "\n"; |
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
name: array-append-benchmark |
||||
build-mode: bin |
||||
optimize: 2 |
||||
sources: |
||||
- benchmark.php |
||||
@ -0,0 +1,28 @@ |
||||
{ |
||||
"count": 100000, |
||||
"checksum": "200000000", |
||||
"samples": { |
||||
"candidate": [ |
||||
0.571479212, |
||||
0.582684347, |
||||
0.558988057, |
||||
0.561899686, |
||||
0.564578895, |
||||
0.560435413, |
||||
0.559544357, |
||||
0.563601629, |
||||
0.566580885 |
||||
], |
||||
"baseline": [ |
||||
0.970465081, |
||||
0.979752269, |
||||
0.98223335, |
||||
0.983323284, |
||||
0.99910999, |
||||
0.976687971, |
||||
0.977219854, |
||||
0.980881162, |
||||
1.018139124 |
||||
] |
||||
} |
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
<?php |
||||
|
||||
require __DIR__ . '/benchmark.php'; |
||||
|
||||
main($argc, $argv); |
||||
@ -0,0 +1,72 @@ |
||||
--TEST-- |
||||
Known-array append expressions preserve values, references and RHS mutations |
||||
--FILE-- |
||||
<?php |
||||
function nextValue(array &$items): int |
||||
{ |
||||
$items[] = 10; |
||||
return count($items); |
||||
} |
||||
function main(): void |
||||
{ |
||||
$source = std::any(1); |
||||
$reference =& $source; |
||||
$items = []; |
||||
$result = ($items[] = $reference); |
||||
$reference = 2; |
||||
var_dump($items, $result); |
||||
$items[] =& $reference; |
||||
$reference = 3; |
||||
var_dump($items, $result); |
||||
$copy = $items; |
||||
$value = ($items[] = nextValue($items)); |
||||
var_dump($items, $copy, $value); |
||||
$nested = [&$reference]; |
||||
$last = ($items[] = $nested[0]); |
||||
$reference = 4; |
||||
var_dump($items, $last); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(1) { |
||||
[0]=> |
||||
int(1) |
||||
} |
||||
int(1) |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
&int(3) |
||||
} |
||||
int(1) |
||||
array(4) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
&int(3) |
||||
[2]=> |
||||
int(10) |
||||
[3]=> |
||||
int(3) |
||||
} |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
&int(3) |
||||
} |
||||
int(3) |
||||
array(5) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
&int(4) |
||||
[2]=> |
||||
int(10) |
||||
[3]=> |
||||
int(3) |
||||
[4]=> |
||||
int(3) |
||||
} |
||||
int(3) |
||||
Loading…
Reference in new issue