为什么 Eigens mean() 方法比 sum() 方法快得多?

Why is Eigens mean() method so much faster than sum()?

这是一个相当理论化的问题,但我对此很感兴趣,如果有人对此有一些专业知识并愿意分享,我会很高兴。

我有一个包含 2000 行和 600 列的浮点数矩阵,我想从每一行中减去列的平均值。我测试了以下两行并比较了它们的运行时间:

MatrixXf centered = data.rowwise() - (data.colwise().sum() / data.cols());
MatrixXf centered = data.rowwise() - data.colwise().mean();

我认为,mean() 与将每列的总和除以行数并没有什么不同,但是第一行的执行在我的计算机上需要 12.3 秒,第二行完成在 0.09 秒内。

我正在使用 Eigen version 3.2.6,目前是最新版本,我的矩阵以行优先顺序存储。

是否有人了解 Eigen 的内部结构,这可以解释这种巨大的性能差异?


编辑: 我应该补充一点,上面代码中的 data 实际上是 Eigen::Map< Eigen::MatrixXf<Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > 类型并将 Eigen 的功能映射到原始缓冲区。


编辑 2: 按照 GuyGreer 的建议,我将提供一些示例代码来重现我的发现:

#include <iostream>
#include <chrono>
#include <Eigen/Core>
using namespace std;
using namespace std::chrono;
using namespace Eigen;

int main(int argc, char * argv[])
{
    MatrixXf data(10000, 1000), centered;
    data.setRandom();
    auto start = high_resolution_clock::now();
    if (argc > 1)
        centered = data.rowwise() - data.colwise().mean();
    else
        centered = data.rowwise() - (data.colwise().sum() / data.rows());
    auto stop = high_resolution_clock::now();
    cout << duration_cast<milliseconds>(stop - start).count() << " ms" << endl;
    return 0;
}

编译:

g++ -O3 -std=c++11 -o test test.cc

运行 没有参数的结果程序,因此使用 sum(),在我的机器上需要 126 秒,而 运行 test 1 使用 mean()只需0.03秒!


编辑 3: 事实证明(见评论),不是 sum() 花费了这么长时间,而是结果向量除以数字的行。所以新的问题是:为什么 Eigen 需要超过 2 分钟才能将具有 1000 列的向量除以单个标量?

不知何故,每次都重新计算部分归约(求和)和除法,因为 operator/ 错误地丢失了有关部分归约评估成本的一些关键信息...显式评估均值修复了问题:

centered = data.rowwise() - (data.colwise().sum() / data.cols()).eval();

当然,这个评估应该由 Eigen 为您完成,由变更集固定 42ab43a。此修复将成为下一个 3.2.7 和 3.3 版本的一部分。