[PATCH 07/20] alpha: deepen memchr prefetch distance for EV7

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed Aug 12 20:44:03 GMT 2026



On 11/08/26 22:19, Matt Turner wrote:
> The alpha memchr already issues a read prefetch ahead of the scan, but only
> three cache lines out -- tuned for the 21264 (EV6/67/68) and its external
> Bcache.  On the 21364 (EV7), whose on-chip memory controller has a much longer
> memory latency, three lines is too close to hide the miss.
> 
> Issue the prefetch eight cache lines ahead instead (both the priming prefetches
> before the cacheline loop and the one inside it).  This is a read hint, so on
> the 21264 a deeper distance is at worst dropped; no IMPLVER dispatch is needed.
> 
> Measured on an AlphaServer ES47 (EV7, 1.3GHz), cold cyc/call (full scan):
>   size       before    after
>   4096         3898     3268  1.19x
>   65536       61359    52271  1.17x
>   262144     246666   208148  1.19x
> 
> The 21264 side of that claim has since been measured rather than assumed: on
> an EV68CB the read-prefetch distance curve for a streaming scan is flat from
> four cache lines out to eight, so moving from three to eight neither helps nor
> hurts there.
> 
> An assembly rewrite was prototyped but, with the deeper prefetch, a read-hint
> loop measured no faster than this C.  The only further gain came from a
> modify-intent (LDS/LDT) prefetch, which is not safe to issue on the read-only
> operand of a general-purpose memchr: it requests the line for ownership, which
> is wrong for a buffer the caller may share.  It is also not the win it looks
> like.  On an EV68CB, modify intent on a pure read stream measures 2x slower
> than a plain read prefetch -- for memcmp, 756446 cycles against 368989 on a
> cold 256KB compare -- because ReadBlkMod provokes about twice the Mbox replay
> traps.  Bcache misses are unchanged, so the cost is not the extra coherence
> traffic one might expect; it is the replays.

It would be good if we could *remove* the alpha implementation instead. I am
really not sure if comments:

 97         /* Within each cacheline, advance the load for the next word
 98            before the test for the previous word is complete.  This
 99            allows us to hide the 3 cycle L1 cache load latency.  We
100            only perform this advance load within a cacheline to prevent
101            reading across page boundary.  */

still holds for recent gcc.  These kind of hand-rolled pipelining were usually
done to overcome old compiler limitations.

I think the only major difference is that alpha implementation relies heavily
on software prefetch. And I recall from old arm/powerpc chips this is usually
a hit or miss, and can be a performance loss when uses without care.

So I wonder if can't do something like this instead:

--
diff --git a/string/memchr.c b/string/memchr.c
index 96afaa6f5e2..bdf2074974c 100644
--- a/string/memchr.c
+++ b/string/memchr.c
@@ -20,6 +20,7 @@
 #include <string-fzb.h>
 #include <string-fzc.h>
 #include <string-fzi.h>
+#include <string-prefetch.h>
 #include <string-shift.h>
 #include <string.h>

@@ -64,9 +65,13 @@ __memchr (void const *s, int c_in, size_t n)
   if (word_ptr == lword)
     return NULL;

+  string_prefetch_initial (word_ptr,
+                          (const char *) lword - (const char *) word_ptr);
+
   word = *++word_ptr;
   while (word_ptr != lword)
     {
+      string_prefetch (word_ptr);
       if (has_eq (word, repeated_c))
        return (char *) word_ptr + index_first_eq (word, repeated_c);
       word = *++word_ptr;
--

With a generic implementation being no-ops and alpha being something like:

  #define STRING_PREFETCH_AHEAD   192
  #define STRING_PREFETCH_STRIDE  64

  static __always_inline void
  string_prefetch_initial (const void *s, size_t len)
  {
    if (len >= STRING_PREFETCH_AHEAD)
      for (unsigned int i = STRING_PREFETCH_STRIDE;
           i < STRING_PREFETCH_AHEAD; i += STRING_PREFETCH_STRIDE)
        __builtin_prefetch ((const char *) s + i, 0, 3);
  }

  static __always_inline void
  string_prefetch (const void *s)
  {
    __builtin_prefetch ((const char *) s + STRING_PREFETCH_AHEAD, 0, 3);
  }

And might want to play with some -funroll-loops.


> ---
>  sysdeps/alpha/memchr.c | 15 ++++++++++-----
>  1 file changed, 10 insertions(+), 5 deletions(-)
> 
> diff --git ./sysdeps/alpha/memchr.c ./sysdeps/alpha/memchr.c
> index d9115923d2..663cd08454 100644
> --- ./sysdeps/alpha/memchr.c
> +++ ./sysdeps/alpha/memchr.c
> @@ -79,10 +79,15 @@ __memchr (const void *s, int xc, size_t n)
>    /* If the block is sufficiently large, align to cacheline and prefetch.  */
>    if (unlikely (n >= 256))
>      {
> -      /* Prefetch 3 cache lines beyond the one we're working on.  */
> -      prefetch (s_align + 8);
> -      prefetch (s_align + 16);
> +      /* Prefetch several cache lines beyond the one we're working on.
> +	 The 21364 (EV7), with its on-chip memory controller and much longer
> +	 memory latency than the 21264's external Bcache, needs the read
> +	 prefetch issued considerably further ahead to hide the miss; eight
> +	 lines beats the three that suit the 21264, and on the 21264 a deeper
> +	 read hint is at worst dropped.  */
>        prefetch (s_align + 24);
> +      prefetch (s_align + 40);
> +      prefetch (s_align + 56);
>  
>        while ((word)s_align & 63)
>  	{
> @@ -120,10 +125,10 @@ __memchr (const void *s, int xc, size_t n)
>  	} while (0)
>  
>        /* While there's still lots more data to potentially be read,
> -	 continue issuing prefetches for the 4th cacheline out.  */
> +	 continue issuing prefetches for the 8th cacheline out.  */
>        while (n >= 256)
>  	{
> -	  prefetch (s_align + 24);
> +	  prefetch (s_align + 64);
>  	  CACHELINE_LOOP;
>  	}
>  



More information about the Libc-alpha mailing list