Repository navigation
ext/standard: Speed up strict in_array()/array_search() with a string needle - #25280
Conversation
|
Callgrind measures the instructions as if it operates as an in-order CPU, and treats all instructions as equally expensive. |
|
@ndossche Here are wall-clock numbers with hyperfine instead. The main motivation: a strict check should be at least as fast as a loose one, and right now it's slower. Benchmark scriptRelease build (
<?php
[$_, $mode, $size] = $argv;
$size = (int)$size;
$list = [];
for ($i = 0; $i < $size; $i++) {
$list[] = "item_{$i}_xxxxxxxx";
}
$needle = 'item_' . ($size - 1) . '_xxxxxxxY';
$calls = intdiv(20_000_000, $size);
$hits = 0;
if ($mode === 'strict') {
for ($i = 0; $i < $calls; $i++) {
if (in_array($needle, $list, true)) $hits++;
}
} else {
for ($i = 0; $i < $calls; $i++) {
if (in_array($needle, $list)) $hits++;
}
}
echo $hits, "\n";
Note: I updated the branch but only to resolve the conflict in NEWS, all the rest is still the same. |
65fb94a to
9f15a48
Compare
ndossche
left a comment
There was a problem hiding this comment.
Thanks, please add an entry to UPGRADING under "performance improvements"
9f15a48 to
2f5f818
Compare
|
UPGRADING entry added, thanks for the review! |
|
Merged,thanks! |
In 107ae86 specialized comparisons were added to in_array() for integer and string needles, to avoid repeating the type checks on every iteration. That covered loose mode only. Strict mode kept going through the generic identity check for every element.
Later @nikic noticed the gap for integers: it doesn't make sense that strict=false is much faster than strict just for lack of a specialized codepath, and added an integer loop for strict mode (ea86a92). Strings were not touched, so strict mode with a string needle still calls
zend_is_identical()for every element.The result is that the same gap is still there for strings. On master in_array($str, $list, true) is about 2x slower than the loose version when the needle isn't found (1.7x in instructions, 2.0x in time).
This PR adds the missing loop, next to the integer one. After it, strict and loose are on par for string needles (about 21 vs 23 instructions per element).
Instructions per call, array of N strings (callgrind, release build):
At N=500 the time goes from 95 to 62 ms (found) and from 79 to 40 ms (not found). Other needle types are unchanged, except null, which costs 3 more instructions per call.