我正在求解相对较小的NxN矩阵(维度小于50)的特征值,并且想知道我在基准测试中是否做了一些愚蠢的事情,Eigen的特征值求解器似乎相当慢。这是驱动程序功能:
Eigen::MatrixXd eigs_speed_test(std::vector<std::size_t> &Nvec, std::size_t Nrepeats) {
Eigen::MatrixXd results(Nvec.size(), 3);
for (std::size_t i = 0; i < Nvec.size(); ++i)
{
Eigen::MatrixXd A = Eigen::MatrixXd::Random(Nvec[i], Nvec[i]);
double maxeig;
auto startTime = std::chrono::system_clock::now();
for (int i = 0; i < Nrepeats; ++i) {
maxeig = A.eigenvalues().real().minCoeff();
}
auto endTime = std::chrono::system_clock::now();
auto elap_us = std::chrono::duration<double>(endTime - startTime).count()/Nrepeats*1e6;
results.row(i) << Nvec[i], elap_us, maxeig;
}
return results;
}
我是用python中的一个小pybind11包装模块调用的。它表明,相对较小的矩阵需要大量的时间才能找到特征值:
N average time to find eigenvalues (microseconds)
2 2.9026
4 8.1132
8 25.3879
16 91.3992
32 511.4046
是的,我知道这一切都取决于架构,但这是在一年前的Mac Mini上,我在其他机器上看到类似的数字。我很好奇其他人是否看到类似的行为。
我特别惊讶的是,找到2x2矩阵的特征值需要2微秒,不应该至少快几个数量级吗?在发布模式下编译(我相信-O2
)
更新
我在运行在i7上的ubuntu虚拟机上运行了相同的测试,并获得了与@ggael类似的结果。尝试使用和不使用LAPACKE。
speed_test.cpp:
#include <vector>
#include <chrono>
#include <iostream>
#include "Eigen/Dense"
Eigen::MatrixXd eigs_speed_test(std::vector<std::size_t> &Nvec, std::size_t Nrepeats) {
Eigen::MatrixXd results(Nvec.size(), 3);
for (std::size_t i = 0; i < Nvec.size(); ++i)
{
Eigen::MatrixXd A = Eigen::MatrixXd::Random(Nvec[i], Nvec[i]);
double maxeig;
auto startTime = std::chrono::system_clock::now();
for (int i = 0; i < Nrepeats; ++i) {
maxeig = A.eigenvalues().real().minCoeff();
}
auto endTime = std::chrono::system_clock::now();
auto elap_us = std::chrono::duration<double>(endTime - startTime).count()/Nrepeats*1e6;
results.row(i) << Nvec[i], elap_us, maxeig;
}
return results;
}
int main(){
std::vector<std::size_t> NxN;
for (std::size_t i=2; i < 64; i *= 2){
NxN.push_back(i);
}
Eigen::MatrixXd res = eigs_speed_test(NxN, 10000);
for (std::size_t i =0; i < res.rows(); ++i){
std::cout << res(i,0) << ": " << res(i,1) << " us\n";
}
return EXIT_SUCCESS;
}
我得到了结果
ian@ian-VirtualBox:~/eigs_testing$ clang++ speed_test.cpp -O3 -DNDEBUG -march=native -std=c++11 -Iexternals/Eigen -DEIGEN_USE_LAPACKE -llapacke -o out_LAPACKE
ian@ian-VirtualBox:~/eigs_testing$ clang++ speed_test.cpp -O3 -DNDEBUG -march=native -std=c++11 -Iexternals/Eigen -o out_Eigen
ian@ian-VirtualBox:~/eigs_testing$ ./out_LAPACKE && ./out_Eigen
2: 1.54799 us
4: 4.93369 us
8: 13.225 us
16: 51.7491 us
32: 260.19 us
2: 0.637694 us
4: 4.1682 us
8: 12.6636 us
16: 56.1858 us
32: 265.023 us
答案 0 :(得分:3)
您的基准测试用于非对称矩阵,但如果您的实际问题是对称的,那么最好使用更快的SelfAdjointEigenSolver
,并为2x2和3x3矩阵提供显式例程。尽管如此,以下是我在i7上使用Eigen 3.3,clang -O3 -march=native
得到的性能:
2 -> 0.44479µs
4 -> 4.65588µs
8 -> 20.4203µs
16 -> 79.6082µs
32 -> 440.319µs
64 -> 2455.39µs
128 -> 22890.3µs
使用MKL顺序:
2 -> 1.64614µs
4 -> 6.21343µs
8 -> 20.4486µs
16 -> 72.9411µs
32 -> 375.88µs
64 -> 2124.27µs
128 -> 12342.9µs
因此,您可以看到,对于小矩阵,Eigen表现出较小的开销。相比之下,Eigen的EigenSolver
实现不能随着矩阵大小的增加而很好地扩展。请注意,此行为特定于EigenSolver
,其他一些内置矩阵分解可以很好地扩展(例如,LU,LLT,QR,BDCSVD等)
答案 1 :(得分:1)
Eigen::MatrixXd
的{{1}}调用数值库(lapack之一)'函数,可能是eigenvalues().real()
或其兄弟函数。
https://software.intel.com/en-us/node/521147
您测量的运行时包含各种内部操作,包括调用外部库(eigen)和外部外部库(lapack)的开销。通常,这种开销是常数。对于更大的矩阵,开销可以忽略不计;像几百秒的2us。对于小矩阵,开销在运行时占主导地位,这就是你所看到的。根据机器,它可能是2us; 1us的;谁知道呢。
如果您的问题总是很大,请忽略开销,花时间和计算时间来解决其他问题。
如果您的问题总是很小,甚至必须消除2us开销,您可以选择以下选项:
geev
,例如Matrix<T,Rows,Cols>
。它是一个模板容器,lirbary /编译器将尝试为每个大小找到优化/内联方案。那么,我不认为Eigen实施了lapack操作; Eigen可能已经实施了一些blas操作。此外,释放模式-O2并不重要,因为您正在调用已编译的lapack。