[glibc] string: vectorize strspn single-accept-char case

Adhemerval Zanella via Glibc-cvs <[email protected]>
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=38b2b3f3a89343a873374f039197bacd5b1db09b

commit 38b2b3f3a89343a873374f039197bacd5b1db09b
Author: Matt Turner <[email protected]>
Date:   Fri Aug 14 12:11:35 2026 -0400

    string: vectorize strspn single-accept-char case
    
    The single-accept-character fast path was a byte-at-a-time scalar loop.
    Scan a word at a time instead, using find_ne_all () to locate the first
    byte that differs from the broadcast accept character.  ACCEPT[0] is not
    NUL here, so a NUL byte differs from it and the search for inequality
    also stops at the end of the string.
    
    The word loop compares against the broadcast character directly rather
    than building a mask each iteration, and calls find_ne_all () once at the
    end.  Where find_ne_all () is an exclusive or the compiler already
    generated this, but alpha builds the mask with cmpbge, and the comparison
    halves its loop.
    
    The multi-character case keeps the existing bitmap-table scan.
    
    Checked against strspn () for lengths 0 to 300 at every byte alignment,
    for several accept characters, with the run ended both by NUL and by a
    differing byte.  Run on alpha (EV68CB), powerpc64 big-endian, 32-bit arm,
    aarch64 and x86_64, covering the cmpbge, cmpb, uqsub8 and generic C
    string-fza.h implementations.  The riscv ones were built but not run.
    
    Speedup over the scalar loop it replaces:
    
      length        8    32   128   512    2K    8K   32K
      Alpha EV68  1.3x  3.3x  5.2x 11.7x 15.2x 16.4x 16.9x
      i7-1370P    1.9x  2.8x  5.5x  4.0x  5.2x  6.9x  7.6x
    
    Both are the best of seven timed runs of each implementation, each run
    calibrated to at least 0.3 s so that the millisecond clock granularity on
    alpha does not quantize the result, and built with -falign-functions=64
    so that code placement does not dominate the short lengths.  The i7-1370P
    reaches this code only where SSE4.2 is unavailable, since the generic C
    string-fza.h is what it would use there.
    
    Suggested-by: Wilco Dijkstra <[email protected]>
    Reviewed-by: Wilco Dijkstra  <[email protected]>

Diff:
---
 string/strspn.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/string/strspn.c b/string/strspn.c
index 9b90ae61e7..f8e869e176 100644
--- a/string/strspn.c
+++ b/string/strspn.c
@@ -18,6 +18,9 @@
 #include <string.h>
 #include <stdint.h>
 #include <libc-pointer-arith.h>
+#include <string-fza.h>
+#include <string-fzi.h>
+#include <string-shift.h>
 
 #undef strspn
 #ifndef STRSPN
@@ -33,9 +36,26 @@ STRSPN (const char *str, const char *accept)
     return 0;
   if (__glibc_unlikely (accept[1] == '\0'))
     {
-      const char *a = str;
-      for (; *str == *accept; str++);
-      return str - a;
+      /* Skip the bytes equal to ACCEPT[0] a word at a time.  ACCEPT[0] is
+	 not NUL here, so a NUL byte differs from it and the search for
+	 inequality also stops at the end of the string.  */
+      const uintptr_t s_int = (uintptr_t) str;
+      const op_t *word_ptr = (const op_t *) PTR_ALIGN_DOWN (str, sizeof (op_t));
+      const op_t repeated_c = repeat_bytes (accept[0]);
+
+      op_t word = *word_ptr;
+      find_t mask = shift_find (find_ne_all (word, repeated_c), s_int);
+      if (mask != 0)
+	return index_first (mask);
+
+      /* Comparing the whole word is cheaper than building the mask on
+	 targets where find_ne_all () is more than an exclusive or.  */
+      do
+	word = *++word_ptr;
+      while (word == repeated_c);
+
+      return (const char *) word_ptr - str
+	     + index_first (find_ne_all (word, repeated_c));
     }
 
   /* Use multiple small memsets to enable inlining on most targets.  */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.