[php-src] PR #25115: zend_memnstr: Use a compact Sunday shift table for short needles
| From: | ArtUkrainskiy | Date: | Sat, 31 Oct 2026 22:06:16 +0000 |
| Subject: | [php-src] PR #25115: zend_memnstr: Use a compact Sunday shift table for short needles | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-40161@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/25115
Author: ArtUkrainskiy
I use a 256-byte Sunday shift table for 9–31-byte needles. The compact path requires at least 1024
input bytes remaining. Every shift fits in one byte. The search loop and first-match behavior stay
the same. Short inline searches and the wide-table algorithm remain unchanged.
Measured on a Ryzen 9 5950X with GCC 15.2 and Clang 21.1. Each moderation row checks all 12 banlist
entries without a match. These are generated application fixtures.
Times are microseconds per complete PHP operation, after empty-loop subtraction. Negative deltas
mean less time relative to master.
| Product case, input bytes | GCC master | GCC compact | Time Δ | Clang master | Clang compact |
Time Δ |
|---|--:|--:|--:|--:|--:|--:|
| English moderation post, 1081 B | 2.9496 | 2.6783 | -9.20% | 2.8060 | 2.5239 | -10.05% |
| Russian moderation post, 1041 B | 3.0331 | 2.8469 | -6.14% | 2.8824 | 2.7330 | -5.19% |
| Spanish moderation post, 1086 B | 2.8435 | 2.5977 | -8.65% | 2.7112 | 2.4345 | -10.21% |
| Arabic moderation post, 1036 B | 3.9634 | 3.7680 | -4.93% | 3.8715 | 3.6589 | -5.49% |
| Hindi moderation post, 1091 B | 2.6601 | 2.4853 | -6.57% | 2.5331 | 2.3453 | -7.41% |
| Chinese moderation post, 1039 B | 2.3145 | 2.0789 | -10.18% | 2.1935 | 1.9339 | -11.84% |
| Japanese moderation post, 1069 B | 3.2940 | 3.1229 | -5.20% | 3.1576 | 2.8807 | -8.77% |
| Korean moderation post, 1098 B | 3.1239 | 2.8635 | -8.34% | 2.9995 | 2.7558 | -8.12% |
| Mixed emoji moderation post, 1097 B | 2.6857 | 2.4525 | -8.68% | 2.5714 | 2.3065 | -10.30% |
| English moderation thread, 65548 B | 169.3483 | 157.9180 | -6.75% | 161.4258 | 148.5095 | -8.00% |
| Chinese moderation thread, 65620 B | 149.5828 | 139.0386 | -7.05% | 143.5457 | 131.7750 | -8.20% |
| Split fields on two pipes, 4123 B | 2.5352 | 2.4895 | -1.80% | 2.7295 | 2.6258 | -3.80% |
| Split fields on
::, 4099 B | 2.5143 | 2.5089 | -0.22% | 2.6946 | 2.7033 | +0.32% |
| Parse HTTP headers, 65848 B | 0.8704 | 0.8702 | -0.02% | 0.8524 | 0.8262 | -3.07% |
| Replace template placeholders, 4250 B | 7.0374 | 5.9181 | -15.91% | 6.4798 | 5.5455 | -14.42% |
| Count {{order.id}} slots, 4250 B | 0.9989 | 0.8339 | -16.52% | 0.9404 | 0.7944 |
-15.53% |
Each build and case has three seconds of warmup and ten approximately one-second samples. There is
one paired round, so results are preliminary. Elapsed A/A reached 2.32%. Small movements around this
level are inconclusive. Delimiter and HTTP rows exercise unchanged short-search paths.
Two 32-byte early-match controls take 3–4% more time on GCC. Searching 4096 a bytes
for 63 a bytes followed by b takes 30.70% more time on GCC. That 64-byte
case uses the existing wide table. Profiling records more branch misses, but the precise cause
remains unresolved.
Eight release binaries pass the string PHPT suite across six compiler configurations. Full PHP
ASan/UBSan and extracted differential/mutation checks also pass. Intel elapsed results were excluded
after failed A/A controls. Native ARM, Windows and 32-bit builds have not been tested.
[Benchmark report, cycles, instructions, raw data and
reproduction](https://github.com/ArtUkrainskiy/php-src-bench/tree/main/reports/compact-sunday).