C++ 特征库与小矩阵运算的性能

C++ 特征库与小矩阵运算的性能,c++,performance,blas,eigen3,avx2,C++,Performance,Blas,Eigen3,Avx2,我为我的项目选择了eigenlib,因为我处理很多小规模的向量和矩阵运算。自然地,我在eigenlib中实现了简单的向量矩阵向量积函数,如下所示: const int dims = 2; template <typename T> using Vec = Eigen::Matrix<T, dims, 1>; template <typename T> using Mat = Eigen::Matrix<T, dims, dims>; temp

我为我的项目选择了eigenlib,因为我处理很多小规模的向量和矩阵运算。自然地,我在eigenlib中实现了简单的向量矩阵向量积函数,如下所示:

const int dims = 2;

template <typename T>
using Vec = Eigen::Matrix<T, dims, 1>;

template <typename T>
using Mat = Eigen::Matrix<T, dims, dims>;

template <typename T>
inline auto quadraticMetricNorm(const Vec<T>& x1, const Vec<T>& x2, const Mat<T>& D)
{
    return x1.transpose() * D * x2;
}
从Eigenlib生成的代码要复杂得多(没有AVX2):

lea r8,QWORD PTR D$11[rbp-256]
lea rdx,QWORD PTR$T7[rbp-256]
lea rcx,QWORD PTR$T3[rsp]
呼叫??$?DV$Matrix@N$01$01$0A@$01$01@Eigen@@@?$MatrixBase@V?$Transpose@$$CBV$Matrix@N$01$00$0A@$01$00@Eigen@@@伊根@@@伊根@@QEBA?BV$Product@V?$Transpose@$$CBV$Matrix@N$01$00$0A@$01$00@Eigen@@@“本征”@@V$Matrix@N$01$01$0A@$01$01@2@$0A@@1@AEBV?$MatrixBase@V?$Matrix@N$01$01$0A@$01$01@Eigen@@@1@@Z;特征::矩阵基::运算符*
mov-rcx,rax
lea r8,QWORD PTR x2$9[rbp-256]
lea rdx,QWORD PTR$T5[rsp]
呼叫??美元?DV$Matrix@N$01$00$0A@$01$00@Eigen@@@?$MatrixBase@V?$Product@V?$Transpose@$$CBV$Matrix@N$01$00$0A@$01$00@Eigen@@@“本征”@@V$Matrix@N$01$01$0A@$01$01@2@$0A@@eigent@@@eigent@@QEBA?BV$Product@V?$Product@V?$Transpose@$$CBV$Matrix@N$01$00$0A@$01$00@Eigen@@@“本征”@@V$Matrix@N$01$01$0A@$01$01@2@$0A@@Eigen@@V$Matrix@N$01$00$0A@$01$00@2@$0A@@1@AEBV?百万美元atrixBase@V?$Matrix@N$01$00$0A@$01$00@Eigen@@@1@@Z;特征::矩阵基::运算符*
; 文件…\eigenlib\eigen\src\core\corevaluators.h
; 155::m_data(m.data()),m_outerStride(IsVectorAtCompileTime?0:m.outerStride())
lea rax,QWORD PTR$T10[rbp-240]
mov QWORD PTR$T10[rbp-256],rax
; 文件…\eigenlib\eigen\src\core\cwisebinaryop.h
; 105::MU lhs(aLhs),MU rhs(aRhs),MU函子(func)
movups xmm0,XMMWORD PTR$T5[rsp]
movups XMMWORD PTR$T6[rsp+8],xmm0
mov rax,QWORD PTR$T5[rsp+16]
mov QWORD PTR$T6[rbp-232],rax
; 文件…\eigenlib\eigen\src\core\redux.h
; 453:return-derived().redux(Eigen::internal::scalar_sum_op());
lea rdx,QWORD PTR$T1[rsp]
lea rcx,QWORD PTR$T6[rsp]
呼叫??$redux@U?$scalar_和_op@NN@internal@Eigen@@@?$DenseBase@V?$CwiseBinaryOp@U?$scalar_乘积_op@NN@internal@Eigen@@$$CBV?$转置@$$CBV$Product@V?$Transpose@$$CBV$Matrix@N$01$00$0A@$01$00@Eigen@@@“本征”@@V$Matrix@N$01$01$0A@$01$01@2@$0A@@@Eigen@@@3@$$CBV$Matrix@N$01$00$0A@$01$00@3@@特征@@@特征@@QEBANAEBU?$scalar\u和_op@NN@internal@1@@Z;特征::DenseBase::redux
movsd QWORD PTR$T10[rbp-240],xmm0

因此Visual Studio和eGenlib的合作并不十分融洽?

请给出一个代码示例。对于clang,两个版本都需要180ms(Haswell 2.6GHz),但在使用此类基准测试时要小心:1)激进的编译器;2)两个时间戳内的计算太少。我建议您在生成的程序集中查找差异,没有理由让它们不同(除了MSVC不是很好…)顺便说一句,编写
x1.dot(D*x2)
可能有助于MSVC完成其工作,但我真的认为您缺少一个编译器选项。MSVC从未真正擅长内联,但我从未见过如此糟糕的事情。或者,他们在2017年版中完全重写了内联启发式,但存在严重缺陷……如果您使用
quadraticMetricNorm
return
T
而不是
auto
,您能试试会发生什么吗?(通常,避免使用
auto
获取特征值运算的结果)chtz的注释是相关的,尤其是在使用返回表达式的
.transpose()*
版本时。在修复代码以防止编译器删除所有内容后(我添加了
return arr[argc];
),由于对Eigen版本进行了显式矢量化,我得到了Eigen 241ms对手写代码263ms。回想一下,在这241ms中,180ms用于调用
高分辨率时钟
,因此相对差异为61ms vs 83ms。
template <typename T>
inline auto quadraticMetricNorm(const Vec<T>& x1, const Vec<T>& x2, const Mat<T>& D)
{
    return (x1(0, 0) * D(0, 0) + x1(1, 0) * D(1, 0)) * x2(0, 0) 
         + (x1(0, 0) * D(0, 1) + x1(1, 0) * D(1, 1)) * x2(1, 0);
}
int main()
{
    random_device rd;
    mt19937 mt(rd());
    uniform_real_distribution<double> dist(1.0, 10.0);

    std::chrono::nanoseconds total{};
    const uint64_t max_loops = 10000000;
    std::vector<double> arr(max_loops);

    for (uint64_t i = 0; i < max_loops; i++)
    {
        Vec<double> x1;
        x1 << dist(mt), dist(mt);

        Vec<double> x2;
        x2 << dist(mt), dist(mt);

        Mat<double> D;
        D << dist(mt), dist(mt), dist(mt), dist(mt);

        auto start = chrono::high_resolution_clock::now();
        arr[i] = quadraticMetricNorm(x1, x2, D);
        total += chrono::duration_cast<std::chrono::nanoseconds>(chrono::high_resolution_clock::now() - start);
    }

    cout << "Loop took " << total.count() / 1000000 << "ms" << endl;

    return 0;
}
addsd   xmm1, xmm0
mulsd   xmm1, xmm10
mulsd   xmm12, xmm7
mulsd   xmm13, xmm9
addsd   xmm12, xmm13
mulsd   xmm12, xmm11
addsd   xmm1, xmm12
lea r8, QWORD PTR D$11[rbp-256]
lea rdx, QWORD PTR $T7[rbp-256]
lea rcx, QWORD PTR $T3[rsp]
call    ??$?DV?$Matrix@N$01$01$0A@$01$01@Eigen@@@?$MatrixBase@V?$Transpose@$$CBV?$Matrix@N$01$00$0A@$01$00@Eigen@@@Eigen@@@Eigen@@QEBA?BV?$Product@V?$Transpose@$$CBV?$Matrix@N$01$00$0A@$01$00@Eigen@@@Eigen@@V?$Matrix@N$01$01$0A@$01$01@2@$0A@@1@AEBV?$MatrixBase@V?$Matrix@N$01$01$0A@$01$01@Eigen@@@1@@Z ; Eigen::MatrixBase<Eigen::Transpose<Eigen::Matrix<double,2,1,0,2,1> const > >::operator*<Eigen::Matrix<double,2,2,0,2,2> >
mov rcx, rax
lea r8, QWORD PTR x2$9[rbp-256]
lea rdx, QWORD PTR $T5[rsp]
call    ??$?DV?$Matrix@N$01$00$0A@$01$00@Eigen@@@?$MatrixBase@V?$Product@V?$Transpose@$$CBV?$Matrix@N$01$00$0A@$01$00@Eigen@@@Eigen@@V?$Matrix@N$01$01$0A@$01$01@2@$0A@@Eigen@@@Eigen@@QEBA?BV?$Product@V?$Product@V?$Transpose@$$CBV?$Matrix@N$01$00$0A@$01$00@Eigen@@@Eigen@@V?$Matrix@N$01$01$0A@$01$01@2@$0A@@Eigen@@V?$Matrix@N$01$00$0A@$01$00@2@$0A@@1@AEBV?$MatrixBase@V?$Matrix@N$01$00$0A@$01$00@Eigen@@@1@@Z ; Eigen::MatrixBase<Eigen::Product<Eigen::Transpose<Eigen::Matrix<double,2,1,0,2,1> const >,Eigen::Matrix<double,2,2,0,2,2>,0> >::operator*<Eigen::Matrix<double,2,1,0,2,1> >
; File ...\eigenlib\eigen\src\core\coreevaluators.h

; 155  :     : m_data(m.data()), m_outerStride(IsVectorAtCompileTime ? 0 : m.outerStride()) 

lea rax, QWORD PTR $T10[rbp-240]
mov QWORD PTR $T10[rbp-256], rax
; File ...\eigenlib\eigen\src\core\cwisebinaryop.h

; 105  :       : m_lhs(aLhs), m_rhs(aRhs), m_functor(func)

movups  xmm0, XMMWORD PTR $T5[rsp]
movups  XMMWORD PTR $T6[rsp+8], xmm0
mov rax, QWORD PTR $T5[rsp+16]
mov QWORD PTR $T6[rbp-232], rax
; File ...\eigenlib\eigen\src\core\redux.h

; 453  :   return derived().redux(Eigen::internal::scalar_sum_op<Scalar,Scalar>());

lea rdx, QWORD PTR $T1[rsp]
lea rcx, QWORD PTR $T6[rsp]
call    ??$redux@U?$scalar_sum_op@NN@internal@Eigen@@@?$DenseBase@V?$CwiseBinaryOp@U?$scalar_product_op@NN@internal@Eigen@@$$CBV?$Transpose@$$CBV?$Product@V?$Transpose@$$CBV?$Matrix@N$01$00$0A@$01$00@Eigen@@@Eigen@@V?$Matrix@N$01$01$0A@$01$01@2@$0A@@Eigen@@@3@$$CBV?$Matrix@N$01$00$0A@$01$00@3@@Eigen@@@Eigen@@QEBANAEBU?$scalar_sum_op@NN@internal@1@@Z ; Eigen::DenseBase<Eigen::CwiseBinaryOp<Eigen::internal::scalar_product_op<double,double>,Eigen::Transpose<Eigen::Product<Eigen::Transpose<Eigen::Matrix<double,2,1,0,2,1> const >,Eigen::Matrix<double,2,2,0,2,2>,0> const > const ,Eigen::Matrix<double,2,1,0,2,1> const > >::redux<Eigen::internal::scalar_sum_op<double,double> >
movsd   QWORD PTR $T10[rbp-240], xmm0