Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions benchmark/array-append/README.md
Original file line number Diff line number Diff line change
@@ -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.
15 changes: 15 additions & 0 deletions benchmark/array-append/benchmark.php
Original file line number Diff line number Diff line change
@@ -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";
}
5 changes: 5 additions & 0 deletions benchmark/array-append/project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: array-append-benchmark
build-mode: bin
optimize: 2
sources:
- benchmark.php
28 changes: 28 additions & 0 deletions benchmark/array-append/results-arm64.json
Original file line number Diff line number Diff line change
@@ -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
]
}
}
5 changes: 5 additions & 0 deletions benchmark/array-append/run.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

require __DIR__ . '/benchmark.php';

main($argc, $argv);
2 changes: 2 additions & 0 deletions phpunit/code/hot-path-codegen.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ function hotPathCodegen(int $limit): int

$items[0] = $value;
$items[] = $value;
$items[] = hotPathTrace('append');
$appendResult = ($items[] = $value);
$items[0] += $value;
$items[0] += $other[0];
$items[2] = $other[0];
Expand Down
8 changes: 8 additions & 0 deletions phpunit/src/HotPathCodegenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ public function testKnownArrayStatementWritesAvoidResultTemporaries(): void
self::assertStringContainsString('items.offsetSet(0L,', $code);
}

public function testKnownArrayAppendFallbackAvoidsDynamicDispatch(): void
{
$code = $this->compileFixture();

self::assertStringNotContainsString('items.offsetSet(php::null,', $code);
self::assertMatchesRegularExpression('/items\.append\(tmp_var_\d+\)/', $code);
}

public function testSafeTwoOperandConcatAndExactStringArgumentStayUnboxed(): void
{
$code = $this->compileFixture();
Expand Down
3 changes: 3 additions & 0 deletions src/Parser/AssignOpTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ protected function parseAssignArrayDim(
return $code . $array . '.appendValue(' . $value . ')';
}
$tmp = $this->addTmpVar(Type::VAR);
if ($arrayType === Type::ARRAY) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.append({$tmp})" . '), ' . $tmp . ')';
}
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')';
}
$dim = $this->parseIdentifier($left->dim);
Expand Down
72 changes: 72 additions & 0 deletions tests/compiler/operator/array-append-expression.phpt
Original file line number Diff line number Diff line change
@@ -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