Merge pull request #30045 from pratham-mcw:svd_opt

core: add ArmPL HAL backend for cv::SVD::compute - #30045

## Summary

- Adds an ARM Performance Library (ARMPL) HAL backend for cv::SVD::compute (cv_hal_SVD32f / cv_hal_SVD64f), replacing OpenCV's default SVD path with ARMPL's LAPACKE_sgesdd / LAPACKE_dgesdd for float and double matrices on AArch64. 

## Changes
**hal/armpl/include/armpl_hal_core.hpp:**

- Declare armpl_hal_SVD32f / armpl_hal_SVD64f and register them as cv_hal_SVD32f / cv_hal_SVD64f.

**hal/armpl/src/armpl_hal_core.cpp:**

- Add a templated armpl_svd helper that maps OpenCV's CV_HAL_SVD_NO_UV / SHORT_UV / MODIFY_A / FULL_UV flags onto LAPACKE's jobz parameter and calls LAPACKE_sgesdd / dgesdd.
- Fall back to OpenCV's default (non-HAL) implementation for small matrices (m < 33, ARMPL_SVD_SMALL_MATRIX_THRESH).
## Performance Benchmarks
<img width="842" height="422" alt="image" src="https://github.com/user-attachments/assets/12b84943-be57-4bf4-9d7e-c85121da4689" />
This commit is contained in:
Pratham Kumar
2026-09-24 09:22:41 +03:00
committed by GitHub
parent c7b9dc388c
commit f46dd2af02
3 changed files with 95 additions and 1 deletions
+8
View File
@@ -108,6 +108,14 @@ int armpl_hal_gemm64fc(const double* src1, size_t src1_step, const double* src2,
#undef cv_hal_gemm64fc
#define cv_hal_gemm64fc armpl_hal_gemm64fc
int armpl_hal_SVD32f(float* src, size_t src_step, float* w, float* u, size_t u_step, float* vt, size_t vt_step, int m, int n, int flags);
int armpl_hal_SVD64f(double* src, size_t src_step, double* w, double* u, size_t u_step, double* vt, size_t vt_step, int m, int n, int flags);
#undef cv_hal_SVD32f
#define cv_hal_SVD32f armpl_hal_SVD32f
#undef cv_hal_SVD64f
#define cv_hal_SVD64f armpl_hal_SVD64f
#endif // HAVE_ARMPL
#endif // OPENCV_ARMPL_HAL_CORE_HPP
+86
View File
@@ -4,13 +4,16 @@
#include <fftw3.h>
#include <cblas.h>
#include <lapacke.h>
#include <algorithm>
#include <complex>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cmath>
#define ARMPL_GEMM_MIN_WORK_VOLUME 10000
#define ARMPL_SVD_SMALL_MATRIX_THRESH 33
namespace {
@@ -172,6 +175,89 @@ int armpl_hal_gemm64fc(const double *src1, size_t src1_step, const double *src2,
reinterpret_cast<const cplx*>(src3), src3_step, beta, reinterpret_cast<cplx*>(dst), dst_step, m, n, k, flags);
}
namespace {
static inline armpl_int_t
armpl_lapacke_gesdd(char jobz, int m, int n, float *a, int lda, float *s, float *u, int ldu, float *vt, int ldvt)
{
return LAPACKE_sgesdd(LAPACK_COL_MAJOR, jobz, m, n, a, lda, s, u, ldu, vt, ldvt);
}
static inline armpl_int_t
armpl_lapacke_gesdd(char jobz, int m, int n, double *a, int lda, double *s, double *u, int ldu, double *vt, int ldvt)
{
return LAPACKE_dgesdd(LAPACK_COL_MAJOR, jobz, m, n, a, lda, s, u, ldu, vt, ldvt);
}
template <typename fptype> static inline void
armpl_transpose_square_inplace(fptype *a, size_t lda, size_t n)
{
for (size_t i = 0; i < n - 1; i++)
for (size_t j = i + 1; j < n; j++)
std::swap(a[j*lda + i], a[i*lda + j]);
}
template <typename fptype> static inline int
armpl_svd(fptype *src, size_t src_step, fptype *w, fptype *u, size_t u_step, fptype *vt, size_t vt_step, int m, int n, int flags)
{
int lda = (int)(src_step / sizeof(fptype));
int ldu = (int)(u_step / sizeof(fptype));
int ldv = (int)(vt_step / sizeof(fptype));
std::vector<fptype> ubuf;
char jobz = ' ';
if (flags & CV_HAL_SVD_NO_UV)
{
ldv = 1;
jobz = 'N';
}
else if ((flags & CV_HAL_SVD_SHORT_UV) && (flags & CV_HAL_SVD_MODIFY_A))
jobz = 'O';
else if ((flags & CV_HAL_SVD_SHORT_UV) && !(flags & CV_HAL_SVD_MODIFY_A))
jobz = 'S';
else if (flags & CV_HAL_SVD_FULL_UV)
jobz = 'A';
if ((flags & CV_HAL_SVD_MODIFY_A) && (flags & CV_HAL_SVD_FULL_UV))
{
ubuf.resize((size_t)m * m);
u = ubuf.data();
ldu = m;
}
armpl_int_t linfo = armpl_lapacke_gesdd(jobz, m, n, src, lda, w, u, ldu, vt, ldv);
if (linfo != 0)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
if (!(flags & CV_HAL_SVD_NO_UV))
armpl_transpose_square_inplace(vt, ldv, n);
if ((flags & CV_HAL_SVD_MODIFY_A) && (flags & CV_HAL_SVD_FULL_UV))
{
for (int i = 0; i < m; i++)
std::copy(u + i*m, u + i*m + m, src + i*lda);
}
return CV_HAL_ERROR_OK;
}
}
int armpl_hal_SVD32f(float *src, size_t src_step, float *w, float *u, size_t u_step, float *vt, size_t vt_step, int m, int n, int flags)
{
if (m < ARMPL_SVD_SMALL_MATRIX_THRESH)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
return armpl_svd(src, src_step, w, u, u_step, vt, vt_step, m, n, flags);
}
int armpl_hal_SVD64f(double *src, size_t src_step, double *w, double *u, size_t u_step, double *vt, size_t vt_step, int m, int n, int flags)
{
if (m < ARMPL_SVD_SMALL_MATRIX_THRESH)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
return armpl_svd(src, src_step, w, u, u_step, vt, vt_step, m, n, flags);
}
enum ArmPLDFTMode
{
ARMPL_DFT_C2C,
+1 -1
View File
@@ -382,7 +382,7 @@ TEST(Photo_CalibrateDebevec, regression)
diff = diff.mul(1.0f / response);
double max;
minMaxLoc(diff, NULL, &max);
#if defined(__arm__) || defined(__aarch64__)
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
ASSERT_LT(max, 0.25);
#else
ASSERT_LT(max, 0.15);