这是一个相当理论上的问题,但我对它很感兴趣,并且如果有人对他或她愿意分享的一些专业知识感到高兴.
我有一个具有2000行和600列的浮点矩阵,并希望从每行中减去列的平均值.我测试了以下两行并比较了它们的运行时间:
MatrixXf centered = data.rowwise() - (data.colwise().sum() / data.cols());
MatrixXf centered = data.rowwise() - data.colwise().mean();
Run Code Online (Sandbox Code Playgroud)
我想,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;
}
Run Code Online (Sandbox Code Playgroud)
编译:
g++ -O3 -std=c++11 -o test test.cc
Run Code Online (Sandbox Code Playgroud)
运行生成的程序不带参数,所以这是用途sum(),需要126秒我的机器上,运行时test 1使用mean()只需要0.03秒!
编辑3:事实证明(参见注释),它不是sum()花费这么长时间,而是将结果向量除以行数.因此,新的问题是:为什么Eigen需要超过2分钟才能用一个标量划分1000列的向量?
不知何故,每次都重新计算部分减少(总和)和除法,因为关于部分减少的评估成本的一些关键信息被错误地丢失了operator/......显式评估平均值解决了问题:
centered = data.rowwise() - (data.colwise().sum() / data.cols()).eval();
Run Code Online (Sandbox Code Playgroud)
当然,这个评估应该由Eigen完成,由变更集42ab43a修复.此修复程序将成为下一个3.2.7和3.3版本的一部分.