core: vectorize cv::sum for CV_64F (#30582)

* core: vectorize cv::sum for CV_64F

Sum_SIMD has specializations for the 8-, 16- and 32-bit depths, but not for
CV_64F, so cv::sum and cv::mean on a double matrix fall through to the scalar
unrolled loop in sum_. CV_64FC1 is not in TYPICAL_MAT_TYPES, so no perf test
covered it.

The source type is already the accumulator type, so the specialization needs
neither expansion nor conversion: it loads three vectors at the native width and
adds them. The channel limit matches DEFINE_SUM_SIMD_32 because init_maskbuf can
only build the cn == 4 masks once the vector holds four lanes, which rules the
case out on an SSE2 baseline.

Extend sum_wide with the CV_64F rows so the change is measurable.

* core: reuse DEFINE_SUM_SIMD_16 for CV_64F

Drop the near-duplicate DEFINE_SUM_SIMD_64 and instantiate DEFINE_SUM_SIMD_16
with v_float64 instead, as suggested in review.

The one thing that kept the two apart was the channel guard. DEFINE_SUM_SIMD_16
rejects cn > 4, which is correct only while the vector holds at least four lanes:
init_maskbuf builds the cn == 4 masks under that condition, so with a narrower
vector the reduction runs against all-zero masks and drops the vectorized
elements without reporting anything. Every 32-bit register type has four lanes or
more, so the existing instantiations never saw it; v_float64 has two on an SSE2
baseline.

Widening the guard to the DEFINE_SUM_SIMD_32 form leaves the existing
instantiations unchanged, since 3+(simd_width>=4) is 4 for all of them.
This commit is contained in:
pranayr710
2026-11-08 08:31:23 +05:30
committed by GitHub
parent 4288cda8af
commit 1780d20b80
2 changed files with 12 additions and 5 deletions

View File

@@ -4,11 +4,12 @@ namespace opencv_test
{
using namespace perf;
// The 32-bit integer depths are not in TYPICAL_MAT_TYPES, so they were not measured; they are the
// ones whose scalar accumulation step this touches.
// The wide depths are not in TYPICAL_MAT_TYPES, so they were not measured; they are the ones
// whose scalar accumulation step this touches.
PERF_TEST_P(Size_MatType, sum_wide,
testing::Combine(testing::Values(TYPICAL_MAT_SIZES),
testing::Values(CV_32SC1, CV_32UC1, CV_32SC3)))
testing::Values(CV_32SC1, CV_32UC1, CV_32SC3,
CV_64FC1, CV_64FC3)))
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());

View File

@@ -124,6 +124,10 @@ template<> struct Sum_SIMD<T, ST> \
ST maskbuf[VTraits<VecT>::max_nlanes*9]; \
};
// The channel limit is 4 only once the vector holds at least four lanes: init_maskbuf builds
// the cn == 4 masks under that condition, so a narrower vector would reduce against all-zero
// masks and silently drop the vectorized elements. Every 32-bit register type has four lanes
// or more; v_float64 on an SSE2 baseline has two.
#undef DEFINE_SUM_SIMD_16
#define DEFINE_SUM_SIMD_16(T, ST, iST, VecT, load_op) \
template<> struct Sum_SIMD<T, ST> \
@@ -134,10 +138,10 @@ template<> struct Sum_SIMD<T, ST> \
} \
int operator ()(const T* src, const uchar* mask, ST* dst, int len, int cn) const \
{ \
if (mask || (cn < 1 || cn > 4)) \
int x = 0, simd_width = VTraits<VecT>::vlanes(); \
if (mask || (cn < 1 || cn > 3+(simd_width>=4))) \
return 0; \
len *= cn; \
int x = 0, simd_width = VTraits<VecT>::vlanes(); \
VecT m0 = vx_load(maskbuf), m1, m2, m3, m4, m5, m6, m7, m8; \
if (cn == 1) { \
m1 = m2 = m3 = m4 = m5 = m6 = m7 = m8 = m0; \
@@ -229,6 +233,8 @@ template<> struct Sum_SIMD<T, ST> \
DEFINE_SUM_SIMD_32(int, double, int64, v_float64)
DEFINE_SUM_SIMD_32(float, double, int64, v_float64)
DEFINE_SUM_SIMD_16(double, double, int64, v_float64, vx_load)
#endif
#endif