1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00

Merge pull request #26887 from kyler1cartesis:4.x

invSqrt SIMD_SCALABLE implementation & HAL tests refactoring #26887

Enable CV_SIMD_SCALABLE for invSqrt.

* Banana Pi BF3 (SpacemiT K1) RISC-V
* Compiler: Syntacore Clang 18.1.4 (build 2024.12)

```
Geometric mean (ms)

                Name of Test                  baseline   simd      simd   
                                                       scalable  scalable
                                                                    vs
                                                                 baseline
                                                                (x-factor)
InvSqrtf::InvSqrtfFixture::(127x61, 32FC1)     0.163    0.051      3.23   
InvSqrtf::InvSqrtfFixture::(127x61, 64FC1)     0.241    0.103      2.35   
InvSqrtf::InvSqrtfFixture::(640x480, 32FC1)    6.460    1.893      3.41   
InvSqrtf::InvSqrtfFixture::(640x480, 64FC1)    9.687    3.999      2.42   
InvSqrtf::InvSqrtfFixture::(1280x720, 32FC1)   19.292   5.701      3.38   
InvSqrtf::InvSqrtfFixture::(1280x720, 64FC1)   29.452   11.963     2.46   
InvSqrtf::InvSqrtfFixture::(1920x1080, 32FC1)  43.326   12.805     3.38   
InvSqrtf::InvSqrtfFixture::(1920x1080, 64FC1)  65.566   26.881     2.44
```
This commit is contained in:
kyler1cartesis
2025-02-19 12:13:48 +03:00
committed by GitHub
parent 6092499907
commit d32d4da9a3
3 changed files with 161 additions and 149 deletions
+17
View File
@@ -706,6 +706,23 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest,
)
);
typedef Size_MatType InvSqrtFixture;
PERF_TEST_P(InvSqrtFixture, InvSqrt, testing::Combine(
testing::Values(TYPICAL_MAT_SIZES),
testing::Values(CV_32FC1, CV_64FC1)))
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
Mat src(sz, type), dst(sz, type);
randu(src, FLT_EPSILON, 1000);
declare.in(src).out(dst);
TEST_CYCLE() cv::pow(src, -0.5, dst);
SANITY_CHECK_NOTHING();
}
///////////// Rotate ////////////////////////
typedef perf::TestBaseWithParam<std::tuple<cv::Size, int, perf::MatType>> RotateTest;
+2 -2
View File
@@ -340,7 +340,7 @@ void invSqrt32f(const float* src, float* dst, int len)
int i = 0;
#if CV_SIMD
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int VECSZ = VTraits<v_float32>::vlanes();
for( ; i < len; i += VECSZ*2 )
{
@@ -368,7 +368,7 @@ void invSqrt64f(const double* src, double* dst, int len)
CV_INSTRUMENT_REGION();
int i = 0;
#if CV_SIMD_64F
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
const int VECSZ = VTraits<v_float64>::vlanes();
for ( ; i < len; i += VECSZ*2)
{
+142 -147
View File
@@ -42,168 +42,163 @@
namespace opencv_test { namespace {
enum
enum HALFunc
{
HAL_EXP = 0,
HAL_LOG = 1,
HAL_SQRT = 2
HAL_SQRT = 2,
HAL_INV_SQRT = 3,
HAL_LU = 4,
HAL_CHOL = 5,
};
TEST(Core_HAL, mathfuncs)
void PrintTo(const HALFunc& v, std::ostream* os)
{
for( int hcase = 0; hcase < 6; hcase++ )
{
int depth = hcase % 2 == 0 ? CV_32F : CV_64F;
double eps = depth == CV_32F ? 1e-5 : 1e-10;
int nfunc = hcase / 2;
int n = 100;
Mat src(1, n, depth), dst(1, n, depth), dst0(1, n, depth);
randu(src, 1, 10);
double min_hal_t = DBL_MAX, min_ocv_t = DBL_MAX;
for( int iter = 0; iter < 10; iter++ )
{
double t = (double)getTickCount();
switch (nfunc)
{
case HAL_EXP:
if( depth == CV_32F )
hal::exp32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::exp64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_LOG:
if( depth == CV_32F )
hal::log32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::log64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_SQRT:
if( depth == CV_32F )
hal::sqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::sqrt64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
default:
CV_Error(Error::StsBadArg, "unknown function");
}
t = (double)getTickCount() - t;
min_hal_t = std::min(min_hal_t, t);
t = (double)getTickCount();
switch (nfunc)
{
case HAL_EXP:
exp(src, dst0);
break;
case HAL_LOG:
log(src, dst0);
break;
case HAL_SQRT:
pow(src, 0.5, dst0);
break;
default:
CV_Error(Error::StsBadArg, "unknown function");
}
t = (double)getTickCount() - t;
min_ocv_t = std::min(min_ocv_t, t);
}
EXPECT_LE(cvtest::norm(dst, dst0, NORM_INF | NORM_RELATIVE), eps);
double freq = getTickFrequency();
printf("%s (N=%d, %s): hal time=%.2fusec, ocv time=%.2fusec\n",
(nfunc == HAL_EXP ? "exp" : nfunc == HAL_LOG ? "log" : nfunc == HAL_SQRT ? "sqrt" : "???"),
n, (depth == CV_32F ? "f32" : "f64"), min_hal_t*1e6/freq, min_ocv_t*1e6/freq);
}
switch (v) {
case HAL_EXP: *os << "HAL_EXP"; return;
case HAL_LOG: *os << "HAL_LOG"; return;
case HAL_SQRT: *os << "HAL_SQRT"; return;
case HAL_INV_SQRT: *os << "HAL_INV_SQRT"; return;
case HAL_LU: *os << "LU"; return;
case HAL_CHOL: *os << "Cholesky"; return;
} // don't use "default:" to emit compiler warnings
}
enum
typedef testing::TestWithParam<std::tuple<int, HALFunc> > mathfuncs;
TEST_P(mathfuncs, accuracy)
{
HAL_LU = 0,
HAL_CHOL = 1
};
const int depth = std::get<0>(GetParam());
const int nfunc = std::get<1>(GetParam());
typedef testing::TestWithParam<int> HAL;
double eps = depth == CV_32F ? 1e-5 : 1e-10;
int n = 100;
TEST_P(HAL, mat_decomp)
{
int hcase = GetParam();
SCOPED_TRACE(cv::format("hcase=%d", hcase));
Mat src(1, n, depth), dst(1, n, depth), dst0(1, n, depth);
randu(src, 1, 10);
switch (nfunc)
{
int depth = hcase % 2 == 0 ? CV_32F : CV_64F;
int size = (hcase / 2) % 4;
size = size == 0 ? 3 : size == 1 ? 4 : size == 2 ? 6 : 15;
int nfunc = (hcase / 8);
#if CV_LASX
double eps = depth == CV_32F ? 1e-5 : 2e-10;
#else
double eps = depth == CV_32F ? 1e-5 : 1e-10;
#endif
case HAL_EXP:
if( depth == CV_32F )
hal::exp32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::exp64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_LOG:
if( depth == CV_32F )
hal::log32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::log64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_SQRT:
if( depth == CV_32F )
hal::sqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::sqrt64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_INV_SQRT:
if( depth == CV_32F )
hal::invSqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::invSqrt64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
if( size == 3 )
return; // TODO ???
Mat a0(size, size, depth), a(size, size, depth), b(size, 1, depth), x(size, 1, depth), x0(size, 1, depth);
randu(a0, -1, 1);
a0 = a0*a0.t();
randu(b, -1, 1);
double min_hal_t = DBL_MAX, min_ocv_t = DBL_MAX;
size_t asize = size*size*a.elemSize();
size_t bsize = size*b.elemSize();
for( int iter = 0; iter < 10; iter++ )
{
memcpy(x.ptr(), b.ptr(), bsize);
memcpy(a.ptr(), a0.ptr(), asize);
double t = (double)getTickCount();
switch (nfunc)
{
case HAL_LU:
if( depth == CV_32F )
hal::LU32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
hal::LU64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
case HAL_CHOL:
if( depth == CV_32F )
hal::Cholesky32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
hal::Cholesky64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
default:
CV_Error(Error::StsBadArg, "unknown function");
}
t = (double)getTickCount() - t;
min_hal_t = std::min(min_hal_t, t);
t = (double)getTickCount();
bool solveStatus = solve(a0, b, x0, (nfunc == HAL_LU ? DECOMP_LU : DECOMP_CHOLESKY));
t = (double)getTickCount() - t;
EXPECT_TRUE(solveStatus);
min_ocv_t = std::min(min_ocv_t, t);
}
//std::cout << "x: " << Mat(x.t()) << std::endl;
//std::cout << "x0: " << Mat(x0.t()) << std::endl;
EXPECT_LE(cvtest::norm(x, x0, NORM_INF | NORM_RELATIVE), eps)
<< "x: " << Mat(x.t())
<< "\nx0: " << Mat(x0.t())
<< "\na0: " << a0
<< "\nb: " << b;
double freq = getTickFrequency();
printf("%s (%d x %d, %s): hal time=%.2fusec, ocv time=%.2fusec\n",
(nfunc == HAL_LU ? "LU" : nfunc == HAL_CHOL ? "Cholesky" : "???"),
size, size,
(depth == CV_32F ? "f32" : "f64"),
min_hal_t*1e6/freq, min_ocv_t*1e6/freq);
default:
CV_Error(Error::StsBadArg, "unknown function");
}
src.copyTo(dst0);
switch (nfunc)
{
case HAL_EXP:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::exp(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::exp(v); });
break;
case HAL_LOG:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::log(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::log(v); });
break;
case HAL_SQRT:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::sqrt(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::sqrt(v); });
break;
case HAL_INV_SQRT:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::pow(v, -0.5f); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::pow(v, -0.5); });
break;
default:
CV_Error(Error::StsBadArg, "unknown function");
}
EXPECT_LE(cvtest::norm(dst, dst0, NORM_INF | NORM_RELATIVE), eps);
}
INSTANTIATE_TEST_CASE_P(Core_HAL, mathfuncs,
testing::Combine(
testing::Values(CV_32F, CV_64F),
testing::Values(HAL_EXP, HAL_LOG, HAL_SQRT, HAL_INV_SQRT)
)
);
typedef testing::TestWithParam<std::tuple<int, HALFunc, int> > mat_decomp;
TEST_P(mat_decomp, accuracy)
{
const int depth = std::get<0>(GetParam());
const int nfunc = std::get<1>(GetParam());
const int size = std::get<2>(GetParam());
#if CV_LASX
double eps = depth == CV_32F ? 1e-5 : 2e-10;
#else
double eps = depth == CV_32F ? 1e-5 : 1e-10;
#endif
Mat a0(size, size, depth), x0(size, 1, depth);
randu(a0, -1, 1);
a0 = a0*a0.t();
randu(x0, -1, 1);
Mat b = a0 * x0;
Mat x = b.clone();
Mat a = a0.clone();
int solveStatus;
switch (nfunc)
{
case HAL_LU:
if( depth == CV_32F )
solveStatus = hal::LU32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
solveStatus = hal::LU64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
case HAL_CHOL:
if( depth == CV_32F )
solveStatus = hal::Cholesky32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
solveStatus = hal::Cholesky64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
default:
CV_Error(Error::StsBadArg, "unknown function");
}
EXPECT_NE(0, solveStatus);
EXPECT_LE(cvtest::norm(a0 * x, b, NORM_INF | NORM_RELATIVE), eps)
<< "x: " << Mat(x.t())
<< "\nx0: " << Mat(x0.t())
<< "\na0: " << a0
<< "\nb: " << b;
}
INSTANTIATE_TEST_CASE_P(Core, HAL, testing::Range(0, 16));
INSTANTIATE_TEST_CASE_P(Core_HAL, mat_decomp,
testing::Combine(
testing::Values(CV_32F, CV_64F),
testing::Values(HAL_LU, HAL_CHOL),
testing::Values(3, 4, 6, 15)
)
);
}} // namespace