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