【问题标题】:Eigen3 matrix multiplication performanceEigen3 矩阵乘法性能
【发布时间】:2015-06-24 13:57:57
【问题描述】:

注意:我也在 E​​igen 论坛 here 上发布了此内容

我想用 3x3 矩阵预乘 3xN 矩阵,即转换 3D 点,例如 p_dest = T * p_source

初始化矩阵后:

Eigen::Matrix<double, 3, Eigen::Dynamic> points = Eigen::Matrix<double, 3, Eigen::Dynamic>::Random(3, NUMCOLS);
Eigen::Matrix<double, 3, Eigen::Dynamic> dest = Eigen::Matrix<double, 3, Eigen::Dynamic>(3, NUMCOLS);
int NT = 100;

我已经评估了这两个版本

// eigen direct multiplication
for (int i = 0; i < NT; i++){
  Eigen::Matrix3d T = Eigen::Matrix3d::Random();
  dest.noalias() = T * points;
}

// col multiplication
for (int i = 0; i < NT; i++){
  Eigen::Matrix3d T = Eigen::Matrix3d::Random();
  for (int c = 0; c < points.cols(); c++){
    dest.col(c) = T * points.col(c);
  }
}

NT 重复只是为了计算平均时间

我很惊讶 逐列 乘法比直接乘法快 4/5 倍 (如果我不使用.noalias(),直接乘法会更慢,但这很好,因为它正在做一个临时复制) 我尝试将 NUMCOLS 从 0 更改为 1000000,并且关系是线性的。

我正在使用 Visual Studio 2013 并在发行版中编译

下图在 X 上显示矩阵的列数,在 Y 上显示单个操作的平均时间,蓝色为 col 乘 col 乘法,红色为矩阵乘法

有什么建议为什么会发生这种情况?

【问题讨论】:

    标签: c++ performance matrix visual-studio-2013 eigen


    【解决方案1】:

    简答

    您正在计时 col 乘法版本中的惰性(因此缺乏)评估,而不是直接版本中的惰性(但已评估)评估。

    长答案

    我们看一下完整的MCVE,而不是代码sn-ps。首先,“you're”版本:

    void ColMult(Matrix3Xd& dest, Matrix3Xd& points)
    {
        Eigen::Matrix3d T = Eigen::Matrix3d::Random();
        for (int c = 0; c < points.cols(); c++){
            dest.col(c) = T * points.col(c);
        }
    }
    
    void EigenDirect(Matrix3Xd& dest, Matrix3Xd& points)
    {
        Eigen::Matrix3d T = Eigen::Matrix3d::Random();
        dest.noalias() = T * points;
    }
    
    int main(int argc, char *argv[])
    {
        srand(time(NULL));
    
        int NUMCOLS = 100000 + rand();
    
        Matrix3Xd points = Matrix3Xd::Random(3, NUMCOLS);
        Matrix3Xd dest   = Matrix3Xd(3, NUMCOLS);
        Matrix3Xd dest2  = Matrix3Xd(3, NUMCOLS);
        int NT = 200;
        // eigen direct multiplication
        auto beg1 = std::chrono::high_resolution_clock::now();
        for (int i = 0; i < NT; i++)
        {
            EigenDirect(dest, points);
        }
        auto end1 = std::chrono::high_resolution_clock::now();
    
        std::chrono::duration<double> elapsed_seconds = end1-beg1;
    
        // col multiplication
        auto beg2 = std::chrono::high_resolution_clock::now();
        for(int i = 0; i < NT; i++)
        {
            ColMult(dest2, points);
        }
    
        auto end2 = std::chrono::high_resolution_clock::now();
    
        std::chrono::duration<double> elapsed_seconds2 = end2-beg2;
        std::cout << "Direct time: " << elapsed_seconds.count() << "\n";
        std::cout << "Col time: " << elapsed_seconds2.count() << "\n";
    
        std::cout << "Eigen speedup: " << elapsed_seconds2.count() / elapsed_seconds.count() << "\n\n";
        return 0;
    }
    

    使用此代码(并打开 SSE),我得到:

    Direct time: 0.449301
    Col time: 0.10107
    Eigen speedup: 0.224949
    

    与您抱怨的相同的 4-5 减速。为什么?!?!在我们得到答案之前,让我们稍微修改一下代码,以便将dest 矩阵发送到ostream。将std::ostream outPut(0); 添加到main() 的开头和 结束计时器之前添加outPut &lt;&lt; dest &lt;&lt; "\n\n";outPut &lt;&lt; dest2 &lt;&lt; "\n\n";std::ostream outPut(0); 不输出任何内容(我很确定坏位已设置),但它确实会导致特征 operator&lt;&lt;called,这会强制评估矩阵。

    注意:如果我们使用了outPut &lt;&lt; dest(1,1),那么dest 的计算结果将仅足以在col 乘法方法中输出单个元素。

    然后我们得到

    Direct time: 0.447298
    Col time: 0.681456
    Eigen speedup: 1.52349
    

    结果符合预期。请注意,Eigen 直接方法花费的时间完全相同(这意味着即使没有添加 ostream 也会进行评估),而 col 方法突然花费的时间要长得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-03
      • 2017-11-06
      • 2019-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多