[PATCH] aarch64: Optimize memcmp for Kunpeng 950 with SVE
Wilco Dijkstra
Wilco.Dijkstra@arm.com
Mon Apr 13 17:11:10 GMT 2026
Hi Weihong,
A few comments/questions:
+ENTRY (__memcmp_kunpeng950)
+ whilelo p0.b, xzr, cnt
+ b.none L(equal)
+ rdvl off_vl, #1
Why use RDVL rather than CNTB? CNTB is faster for simple cases on various
SVE implementations. And if needed you can compute 2x/3x/4x using simple
shifts/adds.
+ ld1b z0.b, p0/z, [src1]
+ ld1b z1.b, p0/z, [src2]
+ cmpne p1.b, p0/z, z0.b, z1.b
+ b.any L(mismatch0)
+
+ whilelo p2.b, off_vl, cnt
+ b.none L(equal)
+ rdvl off_vlx2, #2
+ ld1b z2.b, p2/z, [src1, off_vl]
+ ld1b z3.b, p2/z, [src2, off_vl]
Is there a reason not to use [src1, 1, mul vl] here? It should be cheap.
+ cmpne p3.b, p2/z, z2.b, z3.b
+ b.any L(mismatch1)
Why not reuse z0.b/z1.b/p1.b and branch to mismatch0 rather than repeat the code?
+ whilelo p4.b, off_vlx2, cnt
+ b.none L(equal)
+ rdvl off_vlx3, #3
+ ld1b z6.b, p4/z, [src1, off_vlx2]
+ ld1b z7.b, p4/z, [src2, off_vlx2]
+ cmpne p5.b, p4/z, z6.b, z7.b
+ b.any L(mismatch2)
+
+ whilelo p6.b, off_vlx3, cnt
+ b.none L(equal)
+ rdvl current, #4
+ ld1b z16.b, p6/z, [src1, off_vlx3]
+ ld1b z17.b, p6/z, [src2, off_vlx3]
+ cmpne p7.b, p6/z, z16.b, z17.b
+ b.any L(mismatch3)
Is it really worth doing 4x unrolling here?
+ subs safe_limit, cnt, current
+ csel safe_limit, safe_limit, xzr, hs // Saturate to 0 if cnt < 4VL to prevent unsigned underflow
Rather than use CSEL, we can just do b.lo L(equal) and skip all the tail code.
+ cmp current, safe_limit
+ b.hi L(tail_4xvl)
+
+ ptrue p0.b
No need for this - the first WHILELO p0.b ensures we have all true in p0.
+ .p2align 4
+L(loop_full):
+ addvl off_vl, current, #1
+ addvl off_vlx2, current, #2
+ addvl off_vlx3, current, #3
That's a lot of unnecessary ADDVL - it would be better to increment src1/src2 and
use 1/2/3, mul vl as offset. Or just reuse off_vl, off_vlx2, off_vlx3.
+ ld1b z0.b, p0/z, [src1, current]
+ ld1b z1.b, p0/z, [src2, current]
+ cmpne p1.b, p0/z, z0.b, z1.b
+ b.any L(mismatch0)
+
+ ld1b z2.b, p0/z, [src1, off_vl]
+ ld1b z3.b, p0/z, [src2, off_vl]
+ cmpne p3.b, p0/z, z2.b, z3.b
+ b.any L(mismatch1_full)
It's likely better to compute 2 vectors at a time similar to current memcmp.
Also it's not clear to me why 4x unrolling is used, if we avoid the 3 ADDVL,
is it really faster than 2x unrolling?
+ ld1b z6.b, p0/z, [src1, off_vlx2]
+ ld1b z7.b, p0/z, [src2, off_vlx2]
+ cmpne p5.b, p0/z, z6.b, z7.b
+ b.any L(mismatch2_full)
+
+ ld1b z16.b, p0/z, [src1, off_vlx3]
+ ld1b z17.b, p0/z, [src2, off_vlx3]
+ cmpne p7.b, p0/z, z16.b, z17.b
+ b.any L(mismatch3_full)
+
+ addvl current, current, #4
+ cmp current, safe_limit
+ b.le L(loop_full)
+
+L(tail_4xvl):
+ whilelo p0.b, current, cnt
+ b.none L(equal)
+ addvl off_vl, current, #1
+ ld1b z0.b, p0/z, [src1, current]
+ ld1b z1.b, p0/z, [src2, current]
+ cmpne p1.b, p0/z, z0.b, z1.b
+ b.any L(mismatch0)
+
+ whilelo p2.b, off_vl, cnt
+ b.none L(equal)
+ addvl off_vlx2, current, #2
+ ld1b z2.b, p2/z, [src1, off_vl]
+ ld1b z3.b, p2/z, [src2, off_vl]
+ cmpne p3.b, p2/z, z2.b, z3.b
+ b.any L(mismatch1)
+
+ whilelo p4.b, off_vlx2, cnt
+ b.none L(equal)
+ addvl off_vlx3, current, #3
+ ld1b z6.b, p4/z, [src1, off_vlx2]
+ ld1b z7.b, p4/z, [src2, off_vlx2]
+ cmpne p5.b, p4/z, z6.b, z7.b
+ b.any L(mismatch2)
+
+ whilelo p6.b, off_vlx3, cnt
+ b.none L(equal)
+ ld1b z16.b, p6/z, [src1, off_vlx3]
+ ld1b z17.b, p6/z, [src2, off_vlx3]
+ cmpne p7.b, p6/z, z16.b, z17.b
+ b.any L(mismatch3)
+
+ b L(equal)
Does 4x unrolling the tail code really help performance?
+L(mismatch1_full):
+ brkb p2.b, p0/z, p3.b
+ lasta w0, p2, z2.b
+ lasta w1, p2, z3.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch2_full):
+ brkb p2.b, p0/z, p5.b
+ lasta w0, p2, z6.b
+ lasta w1, p2, z7.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch3_full):
+ brkb p2.b, p0/z, p7.b
+ lasta w0, p2, z16.b
+ lasta w1, p2, z17.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch0):
+ brkb p2.b, p0/z, p1.b
+ lasta w0, p2, z0.b
+ lasta w1, p2, z1.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch1):
+ brkb p0.b, p2/z, p3.b
+ lasta w0, p0, z2.b
+ lasta w1, p0, z3.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch2):
+ brkb p6.b, p4/z, p5.b
+ lasta w0, p6, z6.b
+ lasta w1, p6, z7.b
+ sub x0, x0, x1
+ ret
+
+L(mismatch3):
+ brkb p4.b, p6/z, p7.b
+ lasta w0, p4, z16.b
+ lasta w1, p4, z17.b
+ sub x0, x0, x1
+ ret
That's 7 copies of the same sequence - by using the same register/predicates it should
be feasible to use just 1 copy.
+L(equal):
+ mov x0, #0
+ ret
+END (__memcmp_kunpeng950)
Cheers,
Wilco
More information about the Libc-alpha
mailing list