Merge pull request #29983 from pratham-mcw:gemm-opt

core: accelerate cv::gemm with ARMPL cblas_sgemm/cblas_dgemm - #29983

## Summary
- Adds an ARM Performance Library (ArmPL) HAL backend for cv::gemm (cv_hal_gemm32f / cv_hal_gemm64f), replacing OpenCV's default GEMM path with ArmPL's cblas_sgemm / cblas_dgemm for float and double matrix multiplication on AArch64.

## Changes
**hal/armpl/include/armpl_hal_core.hpp:**
-  declare armpl_hal_gemm32f / armpl_hal_gemm64f and register them as cv_hal_gemm32f / cv_hal_gemm64f. 

**hal/armpl/src/armpl_hal_core.cpp:**
- Add a templated armpl_gemm helper that maps OpenCV's GEMM_1_T / GEMM_2_T / GEMM_3_T flags onto CBLAS transpose/layout arguments and calls cblas_sgemm / cblas_dgemm (row-major).  
-  Fall back to OpenCV's default (non-HAL) implementation for small matrices. (m < 100, ARMPL_GEMM_SMALL_MATRIX_THRESH)   

## Performance Benchmarks
<img width="1080" height="275" alt="image" src="https://github.com/user-attachments/assets/cddce8ce-93ca-4c8e-8b37-a9d42ead0c53" />
This commit is contained in:
Pratham Kumar
2026-09-22 10:33:22 +03:00
committed by GitHub
parent 62751db0d7
commit a91ff70ceb
2 changed files with 188 additions and 0 deletions
+22
View File
@@ -86,6 +86,28 @@ int armpl_hal_dctFree2D(cvhalDFT *context);
#undef cv_hal_dctFree2D
#define cv_hal_dctFree2D armpl_hal_dctFree2D
int armpl_hal_gemm32f(const float* src1, size_t src1_step, const float* src2, size_t src2_step,
float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step,
int m, int n, int k, int flags);
int armpl_hal_gemm64f(const double* src1, size_t src1_step, const double* src2, size_t src2_step,
double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step,
int m, int n, int k, int flags);
int armpl_hal_gemm32fc(const float* src1, size_t src1_step, const float* src2, size_t src2_step,
float alpha, const float* src3, size_t src3_step, float beta, float* dst, size_t dst_step,
int m, int n, int k, int flags);
int armpl_hal_gemm64fc(const double* src1, size_t src1_step, const double* src2, size_t src2_step,
double alpha, const double* src3, size_t src3_step, double beta, double* dst, size_t dst_step,
int m, int n, int k, int flags);
#undef cv_hal_gemm32f
#define cv_hal_gemm32f armpl_hal_gemm32f
#undef cv_hal_gemm64f
#define cv_hal_gemm64f armpl_hal_gemm64f
#undef cv_hal_gemm32fc
#define cv_hal_gemm32fc armpl_hal_gemm32fc
#undef cv_hal_gemm64fc
#define cv_hal_gemm64fc armpl_hal_gemm64fc
#endif // HAVE_ARMPL
#endif // OPENCV_ARMPL_HAL_CORE_HPP
+166
View File
@@ -3,9 +3,175 @@
#include "armpl_hal_core.hpp"
#include <fftw3.h>
#include <cblas.h>
#include <algorithm>
#include <complex>
#include <cstring>
#include <cstdio>
#include <cmath>
#define ARMPL_GEMM_MIN_WORK_VOLUME 10000
namespace {
template <typename fptype> static inline void
armpl_transpose(const fptype *src, size_t src_ld, fptype *dst, size_t dst_ld, size_t m, size_t n)
{
for (size_t i = 0; i < m; i++)
for (size_t j = 0; j < n; j++)
dst[j*dst_ld + i] = src[i*src_ld + j];
}
template <typename fptype> static inline void
armpl_copy_matrix(const fptype *src, size_t src_ld, fptype *dst, size_t dst_ld, size_t m, size_t n)
{
for (size_t i = 0; i < m; i++)
std::copy(src + i*src_ld, src + i*src_ld + n, dst + i*dst_ld);
}
template <typename fptype> static inline void
armpl_set_value(fptype *dst, size_t dst_ld, fptype value, size_t m, size_t n)
{
for (size_t i = 0; i < m; i++)
std::fill(dst + i*dst_ld, dst + i*dst_ld + n, value);
}
static inline void
armpl_cblas_gemm(CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB, int a_m, int d_n, int a_n,
float alpha, const float *src1, int ldsrc1, const float *src2, int ldsrc2,
float beta, float *dst, int lddst)
{
cblas_sgemm(CblasRowMajor, transA, transB, a_m, d_n, a_n, alpha, src1, ldsrc1, src2, ldsrc2, beta, dst, lddst);
}
static inline void
armpl_cblas_gemm(CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB, int a_m, int d_n, int a_n,
double alpha, const double *src1, int ldsrc1, const double *src2, int ldsrc2,
double beta, double *dst, int lddst)
{
cblas_dgemm(CblasRowMajor, transA, transB, a_m, d_n, a_n, alpha, src1, ldsrc1, src2, ldsrc2, beta, dst, lddst);
}
static inline void
armpl_cblas_gemm(CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB, int a_m, int d_n, int a_n,
std::complex<float> alpha, const std::complex<float> *src1, int ldsrc1,
const std::complex<float> *src2, int ldsrc2,
std::complex<float> beta, std::complex<float> *dst, int lddst)
{
cblas_cgemm(CblasRowMajor, transA, transB, a_m, d_n, a_n, &alpha, src1, ldsrc1, src2, ldsrc2, &beta, dst, lddst);
}
static inline void
armpl_cblas_gemm(CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB, int a_m, int d_n, int a_n,
std::complex<double> alpha, const std::complex<double> *src1, int ldsrc1,
const std::complex<double> *src2, int ldsrc2,
std::complex<double> beta, std::complex<double> *dst, int lddst)
{
cblas_zgemm(CblasRowMajor, transA, transB, a_m, d_n, a_n, &alpha, src1, ldsrc1, src2, ldsrc2, &beta, dst, lddst);
}
template <typename elemtype, typename scalartype> static inline int
armpl_gemm_impl(const elemtype *src1, size_t src1_step, const elemtype *src2, size_t src2_step, scalartype alpha,
const elemtype *src3, size_t src3_step, scalartype beta, elemtype *dst, size_t dst_step,
int a_m, int a_n, int d_n, int flags)
{
int ldsrc1 = (int)(src1_step / sizeof(elemtype));
int ldsrc2 = (int)(src2_step / sizeof(elemtype));
int ldsrc3 = (int)(src3_step / sizeof(elemtype));
int lddst = (int)(dst_step / sizeof(elemtype));
int c_m, c_n, d_m;
CBLAS_TRANSPOSE transA, transB;
elemtype eAlpha = (elemtype)alpha;
elemtype eBeta = (elemtype)beta;
transB = (flags & CV_HAL_GEMM_2_T) ? CblasTrans : CblasNoTrans;
d_m = (flags & CV_HAL_GEMM_1_T) ? a_n : a_m;
if (flags & CV_HAL_GEMM_3_T)
{
c_m = d_n;
c_n = d_m;
}
else
{
c_m = d_m;
c_n = d_n;
}
if (flags & CV_HAL_GEMM_1_T)
{
transA = CblasTrans;
std::swap(a_n, a_m);
}
else
{
transA = CblasNoTrans;
}
if (src3 != dst && beta != 0.0 && src3_step != 0)
{
if (flags & CV_HAL_GEMM_3_T)
armpl_transpose(src3, ldsrc3, dst, lddst, c_m, c_n);
else
armpl_copy_matrix(src3, ldsrc3, dst, lddst, c_m, c_n);
}
else if (src3 == dst && (flags & CV_HAL_GEMM_3_T))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
else if (src3_step == 0 && beta != 0.0)
armpl_set_value(dst, lddst, elemtype(), d_m, d_n);
armpl_cblas_gemm(transA, transB, a_m, d_n, a_n, eAlpha, src1, ldsrc1, src2, ldsrc2, eBeta, dst, lddst);
return CV_HAL_ERROR_OK;
}
}
static inline bool armpl_gemm_below_min_volume(int m, int n, int k)
{
return (double)m * n * k < ARMPL_GEMM_MIN_WORK_VOLUME;
}
int armpl_hal_gemm32f(const float *src1, size_t src1_step, const float *src2, size_t src2_step, float alpha,
const float *src3, size_t src3_step, float beta, float *dst, size_t dst_step,
int m, int n, int k, int flags)
{
if (armpl_gemm_below_min_volume(m, n, k))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
return armpl_gemm_impl<float, float>(src1, src1_step, src2, src2_step, alpha, src3, src3_step, beta, dst, dst_step, m, n, k, flags);
}
int armpl_hal_gemm64f(const double *src1, size_t src1_step, const double *src2, size_t src2_step, double alpha,
const double *src3, size_t src3_step, double beta, double *dst, size_t dst_step,
int m, int n, int k, int flags)
{
if (armpl_gemm_below_min_volume(m, n, k))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
return armpl_gemm_impl<double, double>(src1, src1_step, src2, src2_step, alpha, src3, src3_step, beta, dst, dst_step, m, n, k, flags);
}
int armpl_hal_gemm32fc(const float *src1, size_t src1_step, const float *src2, size_t src2_step, float alpha,
const float *src3, size_t src3_step, float beta, float *dst, size_t dst_step,
int m, int n, int k, int flags)
{
if (armpl_gemm_below_min_volume(m, n, k))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
typedef std::complex<float> cplx;
return armpl_gemm_impl<cplx, float>(reinterpret_cast<const cplx*>(src1), src1_step, reinterpret_cast<const cplx*>(src2), src2_step, alpha,
reinterpret_cast<const cplx*>(src3), src3_step, beta, reinterpret_cast<cplx*>(dst), dst_step, m, n, k, flags);
}
int armpl_hal_gemm64fc(const double *src1, size_t src1_step, const double *src2, size_t src2_step, double alpha,
const double *src3, size_t src3_step, double beta, double *dst, size_t dst_step,
int m, int n, int k, int flags)
{
if (armpl_gemm_below_min_volume(m, n, k))
return CV_HAL_ERROR_NOT_IMPLEMENTED;
typedef std::complex<double> cplx;
return armpl_gemm_impl<cplx, double>(reinterpret_cast<const cplx*>(src1), src1_step, reinterpret_cast<const cplx*>(src2), src2_step, alpha,
reinterpret_cast<const cplx*>(src3), src3_step, beta, reinterpret_cast<cplx*>(dst), dst_step, m, n, k, flags);
}
enum ArmPLDFTMode
{
ARMPL_DFT_C2C,