Eigen链式操作性能异常低下:求助原因与规避方案
Eigen链式调用计算行向量方差的性能陷阱问题
在计算大型矩阵行向量的方差时,发现Eigen存在反常表现:全链式调用的性能极低,分步计算中间结果再执行相同操作的速度却快得多,这和Eigen文档/FAQ里建议避免临时变量的内容完全相反。
想知道:
- Eigen有没有需要规避的已知性能陷阱?
- 如何识别会出现这类性能下降的场景?
测试环境与性能差距
测试在Windows环境下进行,分别用MSVC(-O2优化)和MinGW GCC(-O3优化)编译:
- 分步计算版本("row variance with partial eval"):GCC下耗时约560ms,MSVC下约1s
- 全链式调用版本("row variance"):GCC下耗时约90s,MSVC下约104s
两者性能差距极为悬殊,原生for循环的速度应该远快于90秒。
测试代码
#include <iostream> #include <vector> #include <chrono> #include <random> #include <functional> #include "Eigen/Dense" void printTimespan(std::chrono::nanoseconds timeSpan) { using namespace std::chrono; std::cout << "Timing ended:\n" << "\t ms: " << duration_cast<milliseconds>(timeSpan).count() << '\n' << "\t us: " << duration_cast<microseconds>(timeSpan).count() << '\n' << "\t ns: " << timeSpan.count() << '\n'; } class Timer { std::chrono::steady_clock::time_point start_; public: void start() { start_ = std::chrono::steady_clock::now(); } void stop() { timings.push_back((std::chrono::steady_clock::now() - start_).count()); } std::vector<long long> timings; }; std::vector<float> buildBuffer(size_t rows, size_t cols) { std::vector<float> buffer; buffer.reserve(rows * cols); for (size_t i = 0; i < rows; i++) { for (size_t j = 0; j < cols; j++) { buffer.push_back(std::rand() % 1000); } } return buffer; } using EigenArr = Eigen::Array<float, -1, -1, Eigen::RowMajor>; using EigenMap = Eigen::Map<EigenArr>; std::vector<float> benchmark(std::function<EigenArr(const EigenMap&)> func) { constexpr size_t rows = 2000, cols = 200, repetitions = 1000; std::vector<float> buffer = buildBuffer(rows, cols); EigenMap map(buffer.data(), rows, cols); EigenArr res; std::vector<float> means; // 防止编译器因结果未被使用而跳过计算 Timer timer; for (size_t i = 0; i < repetitions; i++) { timer.start(); res = func(map); timer.stop(); means.push_back(res.mean()); } Eigen::Map<Eigen::Vector<long long, -1>> timingsMap(timer.timings.data(), timer.timings.size()); printTimespan(std::chrono::nanoseconds(timingsMap.sum())); return means; } int main() { std::cout << "mean center rows\n"; benchmark([](const EigenMap& map) { return (map.colwise() - map.rowwise().mean()).eval(); }); std::cout << "squared deviations\n"; benchmark([](const EigenMap& map) { return (map.colwise() - map.rowwise().mean()).square().eval(); }); std::cout << "row variance with partial eval\n"; benchmark([](const EigenMap& map) { EigenArr partial = (map.colwise() - map.rowwise().mean()).square().eval(); return (partial.rowwise().sum() / (map.cols() - 1)).eval(); }); std::cout << "row variance\n"; benchmark([](const EigenMap& map) { return ((map.colwise() - map.rowwise().mean()).square().rowwise().sum() / (map.cols() - 1)).eval(); }); }
内容的提问来源于stack exchange,提问作者Janilson
相关产品推荐
相关产品推荐

