加速特征值中的特征值求解

时间:2016-12-12 02:12:16

标签: eigen

我正在求解相对较小的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

2 个答案:

答案 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开销,您可以选择以下选项:

  1. 实施自己。您可以为2x2案例编写单行代码。
  2. 尝试使用大小的特征容器geev,例如Matrix<T,Rows,Cols>。它是一个模板容器,lirbary /编译器将尝试为每个大小找到优化/内联方案。那么,我不认为Eigen实施了lapack操作; Eigen可能已经实施了一些blas操作。
  3. 尝试使用不同的后端。有很多lapack选项,你可以找到低开销的东西。
  4. 尝试降低开销。您可以尝试一些链接器选项来减少动态调度开销。
  5. 此外,释放模式-O2并不重要,因为您正在调用已编译的lapack。