[GIT-PULLS] [php-src] PR #23517: PHP array_shift(): Optimize tail move on hole-free packed arrays
[email protected] (mehmetcansahin)
| Newsgroups | php.git-pulls |
|---|---|
| Message-ID | <sglE9YiGk59Z5yMT15U5Kd9EnZcmBcsDAT31EnBWgoo@main.internal.php.net> |
Pull Request: https://github.com/php/php-src/pull/23517
Author: mehmetcansahin
For a packed array without holes, removing the first element leaves a
contiguous tail. When no iterator is active, `array_shift()` now moves that
tail with a single `memmove()` instead of testing every slot for `IS_UNDEF`;
a one-element tail is moved directly to avoid the call overhead. Packed
arrays with holes, arrays with active iterators, and hash arrays keep the
existing paths.
### Benchmarks
Apple M1, Apple clang 21.0.0, `./configure --disable-all --enable-cli`
(NTS release), `php -n`, best of three in-process samples. Base
`f142b815884`, this PR `fc910a396ea`. Full scripts below.
Draining a packed int array (`$a = $base; while ($a) array_shift($a);`):
| n | repetitions | base | this PR | speedup |
|---:|------------:|-----:|--------:|--------:|
| 100 | 20,000 | 160.5 ms | 58.7 ms | 2.7× |
| 1,000 | 2,000 | 1,075.6 ms | 249.9 ms | 4.3× |
| 10,000 | 200 | 10,726.5 ms | 2,602.4 ms | 4.1× |
Two-element regression check (`$a = [0, 1]; array_shift($a);` x 100M,
exercises the direct-copy specialization): 3.528 s → 3.438 s, no tiny-array
slowdown observed.
<details>
<summary>Benchmark scripts</summary>
Draining benchmark:
```php
<?php
foreach ([100, 1000, 10000] as $n) {
$reps = intdiv(2000000, $n);
$base = range(0, $n - 1);
$best = PHP_FLOAT_MAX;
for ($run = 0; $run < 3; $run++) {
$t = hrtime(true);
for ($r = 0; $r < $reps; $r++) {
$a = $base;
while ($a) array_shift($a);
}
$best = min($best, (hrtime(true) - $t) / 1e6);
}
printf("n=%-6d drain x%-5d best: %8.1f ms\n", $n, $reps, $best);
}
```
Two-element regression check:
```php
<?php
$best = PHP_FLOAT_MAX;
for ($run = 0; $run < 3; $run++) {
$t = hrtime(true);
for ($i = 0; $i < 100000000; $i++) {
$a = [0, 1];
array_shift($a);
}
$best = min($best, (hrtime(true) - $t) / 1e9);
}
printf("fresh [0,1] x100M best: %.3f s\n", $best);
```
</details>
### Tests
`ext/standard/tests/array/`: 856 PASS, 12 SKIP, 0 FAIL/WARN on both the
release NTS build and a Debug+ZTS build (hashtable consistency assertions
enabled). The new test covers packed arrays with holes, which keep the
slot-testing loop.
> Parts of this description were drafted with LLM assistance and reviewed by
> the author, per the "LLM usage in GitHub comments" section of
> CONTRIBUTING.md. All results were produced and verified locally.