Commit 06de9aeef7 for perl
commit 06de9aeef7494843350fdff25194f0dcc5945b5a
Author: Richard Leach <rich+perl@hyphen-dash-hyphen.info>
Date: Sat Oct 3 22:47:02 2026 +0000
S_find_next_masked: boost the word-at-a-time performance
The throughput of this loop was markedly lower than the word-at-a-time
loop in `S_find_span_end_mask`, as can be seen by comparing the
measurements from the previous couple of commits.
This commit tweaks that look to bring throughput of `S_find_next_masked`
up to the level of `S_find_span_end_mask`.
Before:
* find_next_masked /\A[^A]+/ 4741/s
Now:
* find_next_masked /\A[^A]+/ 9261/s
Still trumped by the step up to 512-bit scanning:
* find_next_masked \A[^A]+/ 16365/s
Still nice for the word-at-a-time loop to be zippier when finding the
actual position though.
diff --git a/regexec.c b/regexec.c
index f3896cb924..a3ec5c6ac8 100644
--- a/regexec.c
+++ b/regexec.c
@@ -832,38 +832,18 @@ S_find_next_masked(U8 * s, const U8 * send, const U8 byte, const U8 mask)
PERL_UINTMAX_T word = PERL_COUNT_MULTIPLIER * byte;
PERL_UINTMAX_T mask_word = PERL_COUNT_MULTIPLIER * mask;
+ /* Process per-word as long as we have at least a full word left */
while (s + PERL_WORDSIZE <= send) {
- PERL_UINTMAX_T masked = (* (PERL_UINTMAX_T *) s) & mask_word;
-
- /* If 'masked' contains bytes with the bit pattern of 'byte' within
- * it, xoring with 'word' will leave each of the 8 bits in such
- * bytes be 0, and no byte containing any other bit pattern will be
- * 0. */
- masked ^= word;
-
- /* This causes the most significant bit to be set to 1 for any
- * bytes in the word that aren't completely 0 */
- masked |= masked << 1;
- masked |= masked << 2;
- masked |= masked << 4;
-
- /* The msbits are the same as what marks a byte as variant, so we
- * can use this mask. If all msbits are 1, the word doesn't
- * contain 'byte' */
- if ((masked & PERL_VARIANTS_WORD_MASK) == PERL_VARIANTS_WORD_MASK) {
+ /* "masked" has zero byte where the mask matched. */
+ PERL_UINTMAX_T masked = ((* (PERL_UINTMAX_T *) s) & mask_word) ^ word;
+ /* "hits" has the high bit set where there were any zeroes. */
+ PERL_UINTMAX_T hits = (masked - PERL_COUNT_MULTIPLIER) & ~masked
+ & PERL_VARIANTS_WORD_MASK;
+ if (hits == 0) {
s += PERL_WORDSIZE;
continue;
}
-
- /* Here, the msbit of bytes in the word that aren't 'byte' are 1,
- * and any that are, are 0. Complement and re-AND to swap that */
- masked = ~ masked;
- masked &= PERL_VARIANTS_WORD_MASK;
-
- /* This reduces the problem to that solved by this function */
- s += variant_byte_number(masked);
- return s;
-
+ return s + first_upper_bit_set_byte_number(hits);
}
}