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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user