[PATCH 16/20] alpha: add vectorized single-char fast path for strspn
Matt Turner
mattst88@gmail.com
Wed Aug 12 21:44:52 GMT 2026
On Wed, Aug 12, 2026 at 4:05 PM Adhemerval Zanella Netto
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 11/08/26 22:19, Matt Turner wrote:
> > The generic strspn's single-accept-char case was a byte-at-a-time scalar
> > loop. Alpha has no hand-tuned strspn at all, so add one: the single-char
> > case now scans a word at a time via cmpbge, finding the first byte that
> > differs from the broadcast accept character (a NUL byte always differs
> > from a non-NUL accept char, so this doubles as the string end check with
> > no separate test). The multi-char case keeps the existing bitmap-table
> > scan unchanged -- already efficient, nothing to gain there.
> >
> > Measured on an EV68CB against the old scalar loop: 4.3x faster at 64B,
> > 5.5-5.7x at 256B, ~6.8x at 4KB, ~7.7-7.8x at 64KB, converging toward the
> > 8x ceiling the word width sets. Correctness verified against a reference
> > scan at lengths 0 to 255 and all eight starting byte alignments,
> > including runs that cross a word boundary and strings with a leading
> > mismatch.
>
> Why can't we optimize the generic implementation, instead of adding an
> arch-specific code? Richard Henderson, Wilco, and myself have done a lot
> of optimization on generic code to avoid the need of such code.
>
> The 'cmpbge' is already modeled by find_zero_ne_all (sysdeps/alpha/string-fza.h),
> along with index_first (sysdeps/alpha/string-fzi.h) which will use cttz for
> __alpha_cix__.
>
> So we can expand the generic implementation as:
>
> --
> diff --git a/string/strspn.c b/string/strspn.c
> index 9b90ae61e70..f01f9bbb32c 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-fzc.h>
> +#include <string-fzi.h>
> +#include <string-shift.h>
>
> #undef strspn
> #ifndef STRSPN
> @@ -33,9 +36,24 @@ 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 bytes equal to ACCEPT[0] one word at a time, stopping at the
> + first byte that differs from it. */
> + uintptr_t s_int = (uintptr_t) str;
> + const op_t *word_ptr
> + = (const op_t *) PTR_ALIGN_DOWN (str, sizeof (op_t));
> + op_t repeated_c = repeat_bytes (accept[0]);
> +
> + op_t word = *word_ptr;
> + find_t mask = shift_find (find_zero_ne_all (word, repeated_c), s_int);
> + if (mask != 0)
> + return index_first (mask);
> +
> + do
> + word = *++word_ptr;
> + while (find_zero_ne_all (word, repeated_c) == 0);
> +
> + return (const char *) word_ptr - str
> + + index_first_zero_ne (word, repeated_c);
> }
>
> /* Use multiple small memsets to enable inlining on most targets. */
> --
>
> And this have the extra advantage of optimization not only alpha, but potentially
> all other ABIs that uses the generic implementation.
Absolutely. I'll send a patch against the generic implementation and
remove this patch from the alpha-specific series.
Thanks!
More information about the Libc-alpha
mailing list