Re: [PATCH 09/20] alpha: add EV6/EV7 assembly memcmp

Magnus Lindholm <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <CA+=Fv5Sq7P0AqJe19fe844y3z4Axhth_77VbyOqL1ZJP_SFVNg@mail.gmail.com>
On Wed, Aug 12, 2026 at 3:20 AM Matt Turner <[email protected]> wrote:
>
> Alpha had no assembly memcmp and fell back to the generic C implementation.
> Add one for the alphaev6 directory (shared by the 21264 and 21364).
>
> When both operands share alignment, byte-align to a quadword and then compare
> 64 bytes per iteration, OR-accumulating the per-quad xors; a non-zero
> accumulator means the difference is somewhere in the block, which a byte scan
> from the block base pinpoints to return the correct sign.  Operands of
> differing alignment use a byte compare.
>
> The loop issues a plain read prefetch (LDL to R31) on both streams eight cache
> lines ahead.  On the 21364 (EV7), whose on-chip memory controller has a long
> memory latency, this hides the Bcache misses that otherwise stall the compare;
> on the 21264 it is at worst a dropped hint, so no IMPLVER dispatch is needed.
> A read prefetch -- rather than the modify-intent LDS/LDT used by memset/memcpy
> -- is deliberate: memcmp never writes its operands, so requesting the lines for
> ownership would only add coherence traffic on a multiprocessor.
>
> The 1 to 7 bytes left after the loops are compared with a single masked
> quadword rather than a byte at a time, which would cost about eight issue
> slots per byte and dominate any length that is not a multiple of eight.  Both
> operands are 0mod8 by then, since the head aligned them and everything since
> has advanced by whole quadwords, and an aligned quadword cannot cross a page
> boundary, so a whole one can be loaded at either pointer even though only some
> of it belongs to the compare; masking the difference discards the rest.
> Nothing is stored, so reading past the end of the operands is not observable.
> A mismatch still falls into the byte loop, which is what determines which byte
> differed and in which direction.
>
> Measured on an AlphaServer ES47 (EV7, 1.3GHz), cold cyc/call vs the generic C:
>   size      generic     asm
>   1024         3617    1502  2.35x
>   4096        14527    6423  2.24x
>   65536      231362   96424  2.40x
>   262144     916913  386234  2.37x
>
>
Hi,

The mismatched-alignment fallback is a large regression against the generic C it
replaces. Hot, one operand at offset 3, whole series against baseline:

    size      generic(C)   asm      ratio
    128          89.1    401.3   4.50x slower
    512         268.7   1569.4   5.84x slower
    2048        950.8   6235.3   6.56x slower
    16384      7376.3  50163.0   6.80x slower
    65536     53170.0 237889.3   4.47x slower

0.45 cycles/byte for the C against 3.06 for the byte loop, widening with length.
The generic C does a shifted word-wise compare for unaligned operands; this
gives that up entirely. Co-aligned is a clear win (1.3-2.5x, ~1.7x at 4KB+), so
the patch is worth having -- but the mismatched path needs to keep a word-wise
compare, or defer to the generic implementation, rather than byte-comparing.

Method: both operands filled identically so the compare runs the full length,
one at offset 0 and one at offset 3 from an 8192-aligned base. rpcc timing,
minimum of 7 trials, call overhead measured as a size-0 call and subtracted,
each build measured twice and the runs used as the tolerance. As a check that
does not involve the baseline at all, within the patched build alone at 16KB:
3714 cycles co-aligned against 50163 mismatched, 13.5x for the same library and
the same buffers, which is the two code paths rather than a measurement
artefact. I ran this on my ES40.

Were the quoted 2.2-2.4x numbers all co-aligned?

Magnus
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.