diff --git a/benchmark/array-count/README.md b/benchmark/array-count/README.md new file mode 100644 index 00000000..c3610194 --- /dev/null +++ b/benchmark/array-count/README.md @@ -0,0 +1,34 @@ +# Known-array count benchmark + +Each iteration updates one of 100 array keys and adds `count($items)` to a +checksum. Mutation prevents measuring only a loop-invariant count. This is +an isolated array-update/count workload, not an application-wide benchmark. + +From this directory with PHP 8.5 and a matching PHPX/embed installation: + +```sh +php run.php 30000000 +php ../../bin/tpc.php project.yml --no-progress -o array_count_benchmark +./array_count_benchmark 30000000 +``` + +Expected checksum: `2999995050`. Compile baseline and candidate into separate +binary paths and build directories, keeping PHPX and compiler flags fixed. +Alternate execution order, discard a warm-up pair, and compare medians. + +## Sample result + +Linux ARM64 in Docker, PHP 8.5.10 ZTS, PHPX `6a68f38`, GCC `-O2`, no LTO; +TypePHP baseline `692841a6` versus direct known-array count lowering. +30 million iterations, nine measured pairs after one discarded warm-up: + +| Build | Median | Range | +| --- | ---: | ---: | +| Baseline | 319.63 ms | 301.22–345.40 ms | +| Direct count | 192.73 ms | 185.74–196.94 ms | + +Elapsed time decreased 39.70%; all nine pairs were faster and checksums +matched. Raw samples in seconds are in `results-arm64.json`. Measurements +include process startup and exclude compilation, on a shared development +machine. They do not imply the same application-wide improvement or a +speedup against native PHP. diff --git a/benchmark/array-count/benchmark.php b/benchmark/array-count/benchmark.php new file mode 100644 index 00000000..6f9cf229 --- /dev/null +++ b/benchmark/array-count/benchmark.php @@ -0,0 +1,12 @@ + 1 ? (int) $argv[1] : 10000000; + $items = []; + $checksum = 0; + for ($i = 0; $i < $iterations; ++$i) { + $items[$i % 100] = $i; + $checksum += count($items); + } + echo $checksum, "\n"; +} diff --git a/benchmark/array-count/project.yml b/benchmark/array-count/project.yml new file mode 100644 index 00000000..33fc35fb --- /dev/null +++ b/benchmark/array-count/project.yml @@ -0,0 +1,5 @@ +name: array-count-benchmark +build-mode: bin +optimize: 2 +sources: + - benchmark.php diff --git a/benchmark/array-count/results-arm64.json b/benchmark/array-count/results-arm64.json new file mode 100644 index 00000000..050cf37a --- /dev/null +++ b/benchmark/array-count/results-arm64.json @@ -0,0 +1,28 @@ +{ + "count": 30000000, + "checksum": "2999995050", + "samples": { + "candidate": [ + 0.185741445, + 0.190759172, + 0.192725389, + 0.193156391, + 0.193159849, + 0.186731365, + 0.19180551, + 0.196935488, + 0.193052223 + ], + "baseline": [ + 0.32846526, + 0.330373976, + 0.301215819, + 0.345402035, + 0.31430037, + 0.335472037, + 0.315937627, + 0.306001963, + 0.319634016 + ] + } +} diff --git a/benchmark/array-count/run.php b/benchmark/array-count/run.php new file mode 100644 index 00000000..2ffee2dc --- /dev/null +++ b/benchmark/array-count/run.php @@ -0,0 +1,5 @@ +compileToCpp('count-known-array.php'); + + self::assertStringContainsString('static_cast(items.count())', $cpp); + self::assertSame(4, substr_count($cpp, 'php::fn::count(')); + } + private function compileToCpp(string $file): string { global $translator; diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index c3d2a2fe..2a28700a 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -1329,6 +1329,14 @@ protected function genCount(string $n, Node\Expr\FuncCall $e, array $c): string| $folded = $this->doFoldCountLiteral($e); if ($folded !== false) return $folded; + if (count($e->args) === 1 + && $receiver instanceof Node\Arg + && $this->isVarExpr($receiver->value) + && $this->hasLocalVar($receiver->value->name) + && $this->argumentAlreadyHasExactType($receiver->value, Type::ARRAY) + ) { + return 'static_cast<' . Type::INT . '>(' . $this->getArg($e, 0) . '.count())'; + } if (count($e->args) >= 2) { return 'php::fn::count(' . $this->getArg($e, 0) . ', ' . $this->convertIntExpr($this->getArg($e, 1)) . ')'; } diff --git a/tests/compiler/array/count-known-array.phpt b/tests/compiler/array/count-known-array.phpt new file mode 100644 index 00000000..e35f8862 --- /dev/null +++ b/tests/compiler/array/count-known-array.phpt @@ -0,0 +1,29 @@ +--TEST-- +Known-array count tracks mutation and preserves recursive and dynamic counting +--FILE-- + +--EXPECT-- +int(0) +int(2) +int(4) +int(1) +int(-1) +int(2) +int(1) +int(2)