[PATCH 09/20] alpha: add EV6/EV7 assembly memcmp
Magnus Lindholm
linmag7@gmail.com
Wed Aug 12 18:48:57 GMT 2026
On Wed, Aug 12, 2026 at 3:20 AM Matt Turner <mattst88@gmail.com> 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
More information about the Libc-alpha
mailing list