1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #28782 from kjg0724:reduce-simd-optimization

core: add platform-specific SIMD for cv::reduce REDUCE_SUM

### Pull Request

Rewrite `cv::reduce` SIMD optimization using platform-specific instructions as discussed in #28763.

#### Changes

**Col reduce (dim=1) — horizontal sum per row:**
- ARM DOTPROD: `vdotq_u32()` — single-instruction byte sum, no intermediate flush needed
- ARM AArch64 fallback: `vpaddlq` chain (u8→u16→u32)
- Intel AVX2: `_mm256_sad_epu8()` — 32 bytes → 4×u64 partial sums per cycle
- Intel SSSE3: `_mm_shuffle_epi8` + `_mm_sad_epu8` for cn=4 channel separation
- Intel SSE2: `_mm_sad_epu8` for cn=1
- cn=4: hardware deinterleave (`vld4q_u8` on ARM, shuffle+SAD on Intel)

**Row reduce (dim=0) — vertical accumulation across rows:**
- u16 intermediate accumulator with 256-row flush (halves memory bandwidth vs direct u32)
- ARM AArch64: `vaddw_u8` widening add (single instruction vs expand+add)
- Intel: unpack + add with u16 buffer

**Coverage:** All REDUCE_SUM type combinations — 8U→32S, 8U→32F, 16U→32F, 16S→32F, 32F→32F, 32F→64F, 64F→64F. Non-8U types use universal intrinsics where platform-specific gain is minimal (widening is single-stage, FP has limited alternatives). `REDUCE_AVG` benefits automatically (uses SUM internally).

**Dispatch:** `CV_CPU_DISPATCH` with SSE2/AVX2/NEON_DOTPROD/LASX. Fallback hierarchy: DOTPROD → AArch64 NEON → Universal Intrinsics → scalar.

#### Benchmark (Apple M3 Pro, MacBook Pro 16-inch 2023)

| Path | Type | Speedup vs scalar |
|------|------|-------------------|
| Col reduce (dim=1) | 8UC1 | **3.0–4.5x** |
| Col reduce (dim=1) | 8UC4 | **2.7–5.0x** |
| Col reduce (dim=1) | 32FC1 | 1.7–2.3x |
| Row reduce (dim=0) | 8UC1 | 1.1–1.5x |

Row reduce gains are modest due to memory-bandwidth bound (as expected for vertical accumulation). No regressions on non-target paths (MAX, MIN, SUM2).

#### Testing

- `opencv_test_core --gtest_filter="*Reduce*:*reduce*"` — 483 tests PASSED
- Edge cases: non-aligned dimensions (127×61), cn=2/3 scalar fallback, REDUCE_SUM2 unmodified

#### Files changed

- `modules/core/src/reduce.simd.hpp` — new, platform-dispatched SIMD implementation
- `modules/core/src/reduce.dispatch.cpp` — new, CV_CPU_DISPATCH wrapper
- `modules/core/CMakeLists.txt` — add `NEON_DOTPROD` to dispatch list
- `modules/core/src/matrix_operations.cpp` — wire dispatch functions into ReduceC/R_Invoker
This commit is contained in:
kjg0724
2026-04-21 18:24:38 +09:00
committed by GitHub
parent 9f101a126f
commit 5d88781f74
4 changed files with 1188 additions and 3 deletions
+1
View File
@@ -12,6 +12,7 @@ ocv_add_dispatched_file(mean SSE2 AVX2 LASX)
ocv_add_dispatched_file(merge SSE2 AVX2 LASX)
ocv_add_dispatched_file(split SSE2 AVX2 LASX)
ocv_add_dispatched_file(sum SSE2 AVX2 LASX)
ocv_add_dispatched_file(reduce SSE2 SSSE3 AVX2 NEON_DOTPROD)
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX)
# dispatching for accuracy tests
+24 -3
View File
@@ -341,6 +341,20 @@ cv::Mat cv::Mat::cross(InputArray _m) const
namespace cv
{
typedef void (*ReduceSumFunc)(const Mat& src, Mat& dst);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth);
template <typename T, typename WT, typename Op>
struct ReduceR_SIMD
{
int operator()(const T*, int start, int, WT*, const Op&) const
{
return start;
}
};
template<typename T, typename ST, typename WT, class Op, class OpInit>
class ReduceR_Invoker : public ParallelLoopBody
{
@@ -364,7 +378,8 @@ public:
for( ; --height; )
{
src += srcstep;
i = range.start;
ReduceR_SIMD<T, WT, Op> simd_op;
i = simd_op(src, range.start, range.end, buf, op);
#if CV_ENABLE_UNROLLED
for(; i <= range.end - 4; i += 4 )
{
@@ -806,7 +821,10 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
{
if( op == REDUCE_SUM )
{
if(sdepth == CV_8U && ddepth == CV_32S)
ReduceSumFunc simd_func = getReduceRSumFunc(sdepth, ddepth);
if(simd_func)
func = (ReduceFunc)simd_func;
else if(sdepth == CV_8U && ddepth == CV_32S)
func = reduceSumR8u32s;
else if(sdepth == CV_8U && ddepth == CV_32F)
func = reduceSumR8u32f;
@@ -881,7 +899,10 @@ void cv::reduce(InputArray _src, OutputArray _dst, int dim, int op, int dtype)
{
if(op == REDUCE_SUM)
{
if(sdepth == CV_8U && ddepth == CV_32S)
ReduceSumFunc simd_func = getReduceCSumFunc(sdepth, ddepth);
if(simd_func)
func = (ReduceFunc)simd_func;
else if(sdepth == CV_8U && ddepth == CV_32S)
func = reduceSumC8u32s;
else if(sdepth == CV_8U && ddepth == CV_32F)
func = reduceSumC8u32f;
+30
View File
@@ -0,0 +1,30 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "precomp.hpp"
#include "reduce.simd.hpp"
#include "reduce.simd_declarations.hpp"
namespace cv {
typedef void (*ReduceSumFunc)(const Mat& src, Mat& dst);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth);
ReduceSumFunc getReduceCSumFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceCSumFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
ReduceSumFunc getReduceRSumFunc(int sdepth, int ddepth)
{
CV_INSTRUMENT_REGION();
CV_CPU_DISPATCH(getReduceRSumFunc, (sdepth, ddepth),
CV_CPU_DISPATCH_MODES_ALL);
}
} // namespace cv
File diff suppressed because it is too large Load Diff