【问题标题】:Why is Eigens mean() method so much faster than sum()?为什么 Eigen mean() 方法比 sum() 快得多?
【发布时间】:2015-10-27 19:53:25
【问题描述】:

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

我有一个包含 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 秒,而使用mean() 运行test 1 只需要0.03 秒!


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

【问题讨论】:

  • 您是否在同一次运行中一个接一个地运行这两个计算?如果是这样,可能是缓存问题,请尝试交换顺序。
  • 不,我已经分别测试过了。
  • 我从未使用过 Eigen,因此无法为您提供帮助,但我认为如果您提供一个他们可以运行的端到端测试来证明这对人们会有所帮助你在问什么。这样人们就可以更好地检查自己发生了什么。
  • 为了公平比较,我认为您应该将(data.colwise().sum() / data.cols()); 替换为简单的data.colwise().sum()。这会产生什么效果(--我现在无法测试)?
  • 可能是 GCC 做出的内联决定。我会尝试删除分支并单独测试两个版本。此外,分析应该让您对正在发生的事情有一个很好的了解。

标签: c++ eigen


【解决方案1】:

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

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

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

【讨论】:

  • 完美,谢谢!并为对变更集的引用竖起大拇指。
  • @Callidior 您意识到 ggael 在回答您的问题时就提交了变更集,是吗?我想我们都必须感谢您发现错误。
  • @AviGinsburg 实际上我没有意识到这一点。感谢您向我指出这一点。
  • 是的,感谢 Callidor 发现了这个缺点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-21
  • 2015-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-18
相关资源
最近更新 更多