[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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.