Repository navigation
PHP array_shift(): Optimize tail move on hole-free packed arrays - #23517
mehmetcansahin wants to merge 1 commit into
Conversation
fc910a3 to
0d2cf57
Compare
|
The FreeBSD failure appears to be an unrelated test flake. Could it be rerun? |
6e6acf0 to
7268e32
Compare
|
I measured this on master c65321c (7268e32 merges with conflicts in Change in CPU cycles per operation with the PR, relative to master (negative = fewer cycles, faster); median of five alternating runs:
Draining 100–10,000 ints gets 2.3–6.0× faster here (2.3–2.7× at 100, 5.2–6.0× at 1,000, 3.8–4.1× at 10,000); the description reports 2.7×, 4.3× and 4.1×. A queue of 10,000 gains less than one of 1,000 (−75 … −72% against −84 … −82%) while executing 92–93% fewer instructions (1,000: 90–91%), which is consistent with copy and cache bandwidth becoming the limit when 160 KB move per shift. The paths the PR does not touch (an array with a hole, a hash array) stay at −6 … +2% with instructions within 0.1%, next to −3 … +2% for master against master. Correctness: every return value and the final array (with references) are the same as master on 500,000 random sequences of shifts, unsets, appends, appended references, shifts of copies, string keys and No correctness issues found. LGTM. Full tables, scripts and data: https://github.com/ArtUkrainskiy/php-src-bench/tree/main/reports/pr-23517-array-shift |
When a packed array has no holes and no active iterators, the slot vacated by the shifted element is the only hole, so the tail can be moved down with a single memmove instead of testing every slot for IS_UNDEF and comparing source and destination indexes. A tail of a single element is moved directly instead, avoiding the memmove call overhead on tiny arrays.
7268e32 to
79c0bfc
Compare
|
Thanks @ArtUkrainskiy for the detailed review! |
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 singlememmove()instead of testing every slot forIS_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
Draining a packed int array (
$a = $base; while ($a) array_shift($a);):Two-element regression check (
$a = [0, 1]; array_shift($a);x 100M, exercises the direct-copy specialization): 3.528s → 3.438 s, no tiny-array slowdown observed.Benchmark scripts
Draining benchmark:
Two-element regression check: