Commit 6fe62b8f for openh264
commit 6fe62b8f3fcc6bf34512be7efc7f64e91f510f02
Author: Xiujun Wang <xiujunwang.dev@gmail.com>
Date: Fri Oct 9 19:06:24 2026 +1100
Optimize AArch64 NEON 16x8 and 16x16 SAD (#4041)
Process rows in pairs using byte absolute differences and two independent pairwise accumulation chains. This shortens the dependency chain in the single-candidate encoder SAD routines.
Add C-reference comparisons across positive and negative strides, unaligned offsets, and extreme pixel values.
On an Apple M1, a repeated single-block microbenchmark improves from 2.70 to 2.27 ns/call for 16x8 and from 6.52 to 4.60 ns/call for 16x16. A 64-block scattered benchmark is effectively unchanged; no whole-encoder speedup is claimed.
diff --git a/codec/encoder/core/arm64/pixel_aarch64_neon.S b/codec/encoder/core/arm64/pixel_aarch64_neon.S
index 30ec497a..8ff5d02e 100644
--- a/codec/encoder/core/arm64/pixel_aarch64_neon.S
+++ b/codec/encoder/core/arm64/pixel_aarch64_neon.S
@@ -198,14 +198,23 @@ WELS_ASM_AARCH64_FUNC_BEGIN WelsSampleSad16x8_AArch64_neon
sxtw x3, w3
ld1 {v0.16b}, [x0], x1
ld1 {v1.16b}, [x2], x3
- uabdl v2.8h, v0.8b, v1.8b
- uabal2 v2.8h, v0.16b, v1.16b
-.rept 7
+ uabd v0.16b, v0.16b, v1.16b
+ uaddlp v2.8h, v0.16b
+ ld1 {v3.16b}, [x0], x1
+ ld1 {v4.16b}, [x2], x3
+ uabd v3.16b, v3.16b, v4.16b
+ uaddlp v5.8h, v3.16b
+.rept 3
ld1 {v0.16b}, [x0], x1
ld1 {v1.16b}, [x2], x3
- uabal v2.8h, v0.8b, v1.8b
- uabal2 v2.8h, v0.16b, v1.16b
+ ld1 {v3.16b}, [x0], x1
+ ld1 {v4.16b}, [x2], x3
+ uabd v0.16b, v0.16b, v1.16b
+ uabd v3.16b, v3.16b, v4.16b
+ uadalp v2.8h, v0.16b
+ uadalp v5.8h, v3.16b
.endr
+ add v2.8h, v2.8h, v5.8h
CALC_AND_STORE_SAD
WELS_ASM_AARCH64_FUNC_END
@@ -214,14 +223,23 @@ WELS_ASM_AARCH64_FUNC_BEGIN WelsSampleSad16x16_AArch64_neon
sxtw x3, w3
ld1 {v0.16b}, [x0], x1
ld1 {v1.16b}, [x2], x3
- uabdl v2.8h, v0.8b, v1.8b
- uabal2 v2.8h, v0.16b, v1.16b
-.rept 15
+ uabd v0.16b, v0.16b, v1.16b
+ uaddlp v2.8h, v0.16b
+ ld1 {v3.16b}, [x0], x1
+ ld1 {v4.16b}, [x2], x3
+ uabd v3.16b, v3.16b, v4.16b
+ uaddlp v5.8h, v3.16b
+.rept 7
ld1 {v0.16b}, [x0], x1
ld1 {v1.16b}, [x2], x3
- uabal v2.8h, v0.8b, v1.8b
- uabal2 v2.8h, v0.16b, v1.16b
+ ld1 {v3.16b}, [x0], x1
+ ld1 {v4.16b}, [x2], x3
+ uabd v0.16b, v0.16b, v1.16b
+ uabd v3.16b, v3.16b, v4.16b
+ uadalp v2.8h, v0.16b
+ uadalp v5.8h, v3.16b
.endr
+ add v2.8h, v2.8h, v5.8h
CALC_AND_STORE_SAD
WELS_ASM_AARCH64_FUNC_END
diff --git a/test/encoder/EncUT_Sample.cpp b/test/encoder/EncUT_Sample.cpp
index 24a15e33..be19f6f1 100644
--- a/test/encoder/EncUT_Sample.cpp
+++ b/test/encoder/EncUT_Sample.cpp
@@ -669,6 +669,31 @@ GENERATE_Sad8x16_UT (WelsSampleSad8x16_AArch64_neon, WelsSampleSad8x16_c, WELS_C
GENERATE_Sad16x8_UT (WelsSampleSad16x8_AArch64_neon, WelsSampleSad16x8_c, WELS_CPU_NEON)
GENERATE_Sad16x16_UT (WelsSampleSad16x16_AArch64_neon, WelsSampleSad16x16_c, WELS_CPU_NEON)
+TEST_F (SadSatdAssemblyFuncTest, Sad16_AArch64_neon_StridesAndExtremes) {
+ if (0 == (m_uiCpuFeatureFlag & WELS_CPU_NEON))
+ return;
+ const int32_t kiStrides[] = {16, 23, 32, -16, -23, -32};
+ for (int iPattern = 0; iPattern < 4; ++iPattern) {
+ for (int i = 0; i < (PIXEL_STRIDE << 5); ++i) {
+ m_pPixSrcA[i] = iPattern < 2 ? iPattern * 255 : rand() % 256;
+ m_pPixSrcB[i] = iPattern < 2 ? (1 - iPattern) * 255 :
+ iPattern == 2 ? m_pPixSrcA[i] : rand() % 256;
+ }
+ for (int32_t iStrideA : kiStrides) {
+ for (int32_t iStrideB : kiStrides) {
+ for (int iOffset = 0; iOffset < 16; ++iOffset) {
+ uint8_t* pA = m_pPixSrcA + iOffset + (iStrideA < 0 ? -15 * iStrideA : 0);
+ uint8_t* pB = m_pPixSrcB + 15 - iOffset + (iStrideB < 0 ? -15 * iStrideB : 0);
+ EXPECT_EQ (WelsSampleSad16x8_c (pA, iStrideA, pB, iStrideB),
+ WelsSampleSad16x8_AArch64_neon (pA, iStrideA, pB, iStrideB));
+ EXPECT_EQ (WelsSampleSad16x16_c (pA, iStrideA, pB, iStrideB),
+ WelsSampleSad16x16_AArch64_neon (pA, iStrideA, pB, iStrideB));
+ }
+ }
+ }
+ }
+}
+
GENERATE_Sad4x4_UT (WelsSampleSatd4x4_AArch64_neon, WelsSampleSatd4x4_c, WELS_CPU_NEON)
GENERATE_Sad8x8_UT (WelsSampleSatd8x8_AArch64_neon, WelsSampleSatd8x8_c, WELS_CPU_NEON)
GENERATE_Sad8x16_UT (WelsSampleSatd8x16_AArch64_neon, WelsSampleSatd8x16_c, WELS_CPU_NEON)