mirror of
https://github.com/opencv/opencv.git
synced 2026-09-25 04:09:57 +03:00
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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user