From d89bfa761a86fca5593a136b6596d5a96980dd9c Mon Sep 17 00:00:00 2001 From: yavon007 Date: Tue, 8 Sep 2026 12:46:53 +0800 Subject: [PATCH] perf(compiler): bypass dynamic dispatch for known-array appends --- benchmark/array-append/README.md | 35 +++++++++ benchmark/array-append/benchmark.php | 15 ++++ benchmark/array-append/project.yml | 5 ++ benchmark/array-append/results-arm64.json | 28 ++++++++ benchmark/array-append/run.php | 5 ++ phpunit/code/hot-path-codegen.php | 2 + phpunit/src/HotPathCodegenTest.php | 8 +++ src/Parser/AssignOpTrait.php | 3 + .../operator/array-append-expression.phpt | 72 +++++++++++++++++++ 9 files changed, 173 insertions(+) create mode 100644 benchmark/array-append/README.md create mode 100644 benchmark/array-append/benchmark.php create mode 100644 benchmark/array-append/project.yml create mode 100644 benchmark/array-append/results-arm64.json create mode 100644 benchmark/array-append/run.php create mode 100644 tests/compiler/operator/array-append-expression.phpt diff --git a/benchmark/array-append/README.md b/benchmark/array-append/README.md new file mode 100644 index 00000000..8bb40de1 --- /dev/null +++ b/benchmark/array-append/README.md @@ -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. diff --git a/benchmark/array-append/benchmark.php b/benchmark/array-append/benchmark.php new file mode 100644 index 00000000..6916fcea --- /dev/null +++ b/benchmark/array-append/benchmark.php @@ -0,0 +1,15 @@ + 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"; +} diff --git a/benchmark/array-append/project.yml b/benchmark/array-append/project.yml new file mode 100644 index 00000000..940357d6 --- /dev/null +++ b/benchmark/array-append/project.yml @@ -0,0 +1,5 @@ +name: array-append-benchmark +build-mode: bin +optimize: 2 +sources: + - benchmark.php diff --git a/benchmark/array-append/results-arm64.json b/benchmark/array-append/results-arm64.json new file mode 100644 index 00000000..757b6715 --- /dev/null +++ b/benchmark/array-append/results-arm64.json @@ -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 + ] + } +} diff --git a/benchmark/array-append/run.php b/benchmark/array-append/run.php new file mode 100644 index 00000000..2ffee2dc --- /dev/null +++ b/benchmark/array-append/run.php @@ -0,0 +1,5 @@ +compileFixture(); + + self::assertStringNotContainsString('items.offsetSet(php::null,', $code); + self::assertMatchesRegularExpression('/items\.append\(tmp_var_\d+\)/', $code); + } + public function testSafeTwoOperandConcatAndExactStringArgumentStayUnboxed(): void { $code = $this->compileFixture(); diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index e87ab9f6..2e4e9fd5 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -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); diff --git a/tests/compiler/operator/array-append-expression.phpt b/tests/compiler/operator/array-append-expression.phpt new file mode 100644 index 00000000..f62aa3a2 --- /dev/null +++ b/tests/compiler/operator/array-append-expression.phpt @@ -0,0 +1,72 @@ +--TEST-- +Known-array append expressions preserve values, references and RHS mutations +--FILE-- + +--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)