From 2e80d30df4b1aa9c1b76f746ed36374f4bdae4db Mon Sep 17 00:00:00 2001 From: kjg0724 <63202356+kjg0724@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:33:49 +0900 Subject: [PATCH] imgproc: migrate MomentsInTile_SIMD to universal:scalable intrinsics (#28881) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the ushort specialization from CV_SIMD128 to (CV_SIMD || CV_SIMD_SCALABLE), enabling AVX2/AVX512/RVV widths instead of 128-bit only. uchar specialization left at CV_SIMD128 — initial migration regressed on RVV (Muse Pi v3.0, 0.77~0.85x) when vlanes16 == TILE_SIZE collapses the SIMD loop to one iteration and per-tile setup overhead dominates. - replace v_int32x4/v_uint32x4/v_uint64x2 with scalable v_int32/v_uint32/v_uint64 - iota vector in static const sized by VTraits::max_nlanes (initialized once at program load) - drop buf64; use v_reduce_sum(v_uint64) directly (defined on all backends) - vx_cleanup() at operator() tail for RVV vsetvl hygiene --- modules/imgproc/src/moments.cpp | 71 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/modules/imgproc/src/moments.cpp b/modules/imgproc/src/moments.cpp index 5f910a26fb..3459ede418 100644 --- a/modules/imgproc/src/moments.cpp +++ b/modules/imgproc/src/moments.cpp @@ -255,56 +255,65 @@ struct MomentsInTile_SIMD } }; +#endif // CV_SIMD128 + +#if (CV_SIMD || CV_SIMD_SCALABLE) + +namespace { +template +struct IotaInit { + T CV_DECL_ALIGNED(CV_SIMD_WIDTH) data[N]; + IotaInit() { for (int i = 0; i < N; i++) data[i] = (T)i; } +}; +static const IotaInit::max_nlanes> g_ix0_init; +} + template <> struct MomentsInTile_SIMD { - MomentsInTile_SIMD() - { - // nothing - } + MomentsInTile_SIMD() {} int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3) { int x = 0; + const int vlanes32 = VTraits::vlanes(); + v_int32 v_delta = vx_setall_s32(vlanes32); + v_int32 v_ix0 = vx_load(g_ix0_init.data); + v_uint32 z = vx_setzero_u32(); + v_uint32 v_x0 = z, v_x1 = z, v_x2 = z; + v_uint64 v_x3 = vx_setzero_u64(); + + for ( ; x <= len - vlanes32; x += vlanes32 ) { - v_int32x4 v_delta = v_setall_s32(4), v_ix0 = v_int32x4(0, 1, 2, 3); - v_uint32x4 z = v_setzero_u32(), v_x0 = z, v_x1 = z, v_x2 = z; - v_uint64x2 v_x3 = v_reinterpret_as_u64(z); + v_int32 v_src = v_reinterpret_as_s32(vx_load_expand(ptr + x)); - for( ; x <= len - 4; x += 4 ) - { - v_int32x4 v_src = v_reinterpret_as_s32(v_load_expand(ptr + x)); + v_x0 = v_add(v_x0, v_reinterpret_as_u32(v_src)); + v_x1 = v_add(v_x1, v_reinterpret_as_u32(v_mul(v_src, v_ix0))); - v_x0 = v_add(v_x0, v_reinterpret_as_u32(v_src)); - v_x1 = v_add(v_x1, v_reinterpret_as_u32(v_mul(v_src, v_ix0))); + v_int32 v_ix1 = v_mul(v_ix0, v_ix0); + v_x2 = v_add(v_x2, v_reinterpret_as_u32(v_mul(v_src, v_ix1))); - v_int32x4 v_ix1 = v_mul(v_ix0, v_ix0); - v_x2 = v_add(v_x2, v_reinterpret_as_u32(v_mul(v_src, v_ix1))); + v_ix1 = v_mul(v_ix0, v_ix1); + v_src = v_mul(v_src, v_ix1); + v_uint64 v_lo, v_hi; + v_expand(v_reinterpret_as_u32(v_src), v_lo, v_hi); + v_x3 = v_add(v_x3, v_add(v_lo, v_hi)); - v_ix1 = v_mul(v_ix0, v_ix1); - v_src = v_mul(v_src, v_ix1); - v_uint64x2 v_lo, v_hi; - v_expand(v_reinterpret_as_u32(v_src), v_lo, v_hi); - v_x3 = v_add(v_x3, v_add(v_lo, v_hi)); - - v_ix0 = v_add(v_ix0, v_delta); - } - - x0 = v_reduce_sum(v_x0); - x1 = v_reduce_sum(v_x1); - x2 = v_reduce_sum(v_x2); - v_store_aligned(buf64, v_reinterpret_as_s64(v_x3)); - x3 = buf64[0] + buf64[1]; + v_ix0 = v_add(v_ix0, v_delta); } + x0 = v_reduce_sum(v_x0); + x1 = v_reduce_sum(v_x1); + x2 = v_reduce_sum(v_x2); + x3 = (int64)v_reduce_sum(v_x3); + + vx_cleanup(); return x; } - - int64 CV_DECL_ALIGNED(16) buf64[2]; }; -#endif +#endif // CV_SIMD || CV_SIMD_SCALABLE template #if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9