[php-src] PR #23517: PHP array_shift(): Optimize tail move on hole-free packed arrays

From: Date: Sun, 30 Aug 2026 21:44:42 +0000
Subject: [php-src] PR #23517: PHP array_shift(): Optimize tail move on hole-free packed arrays
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-38227@lists.php.net to get a copy of this message
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.

« previous php.git-pulls (#38227) next »