Eigen + MKL或OpenBLAS比Numpy/Scipy + OpenBLAS慢

3

我目前正在学习C++,希望能够使用矩阵并加速整个过程。之前使用过Python+Numpy+OpenBLAS。

我认为C++ + Eigen + MKL可能会更快,或者至少不会更慢。

以下是我的C++代码:

#define EIGEN_USE_MKL_ALL
#include <iostream>
#include <Eigen/Dense>
#include <Eigen/LU>
#include <chrono>

using namespace std;
using namespace Eigen;

int main()
{
    int n = Eigen::nbThreads( );
    cout << "#Threads: " << n << endl;

    uint16_t size = 4000;
    MatrixXd a = MatrixXd::Random(size,size);

    clock_t start = clock ();
    PartialPivLU<MatrixXd> lu = PartialPivLU<MatrixXd>(a);

    float timeElapsed = double( clock() - start ) / CLOCKS_PER_SEC; 
    cout << "Elasped time is " << timeElapsed << " seconds." << endl ;
}

我的 Python 代码:

import numpy as np
from time import time
from scipy import linalg as la

size = 4000

A = np.random.random((size, size))

t = time()
LU, piv = la.lu_factor(A)
print(time()-t)

我的时间安排:

C++     2.4s
Python  1.2s

C++比Python慢的原因是什么?

我正在使用以下方式编译C++:

g++ main.cpp -o main -lopenblas -O3 -fopenmp  -DMKL_LP64 -I/usr/local/include/mkl/include

MKL绝对起作用了:如果我禁用它,运行时间大约为13秒。

我还尝试了C++ + OpenBLAS,这也给了我大约2.4秒的运行时间。

有什么想法为什么C++和Eigen比numpy/scipy慢?


1
Numpy 实际工作的代码是用 C 写的。如果你想了解速度差异,你需要查看不同底层库使用的算法。编写调用库的程序所使用的语言完全无关紧要。 - Sven Marnach
是的,@SvenMarnach,我并不指望C++更快,我只是期望它们应该有类似的速度,因为它们都应该在后端使用MKL或OpenBLAS。 - Wikunia
在使用-DNDEBUG编译时,所需的时间是多少? - Henri Menke
你的CPU是什么?在这里,使用2.6GHz Core7(Haswell)仅使用Eigen且不使用openmp时,我需要1.4秒,如果使用openmp,则只需要0.7秒。当然,您需要使用-march=native编译以充分利用您的CPU。 - ggael
好的,Eigen和MKL之间的速度比也太大了,一定要使用-march=native编译,并使用Eigen 3.x来利用CPU的AVX指令。由于我这里得到了0.7秒的结果,你应该能得到1秒到最多2秒之间的结果。 - ggael
显示剩余2条评论
2个回答

4
时间不对。这是 墙钟时间 vs. CPU 时间 的典型症状。当我使用来自 <chrono> 头文件的 system_clock 时,它“神奇地”变快了。
#define EIGEN_USE_MKL_ALL
#include <iostream>
#include <Eigen/Dense>
#include <Eigen/LU>
#include <chrono>

int main()
{
    int const n = Eigen::nbThreads( );
    std::cout << "#Threads: " << n << std::endl;

    int const size = 4000;
    Eigen::MatrixXd a = Eigen::MatrixXd::Random(size,size);

    auto start = std::chrono::system_clock::now();

    Eigen::PartialPivLU<Eigen::MatrixXd> lu(a);

    auto stop = std::chrono::system_clock::now();

    std::cout << "Elasped time is "
              << std::chrono::duration<double>{stop - start}.count()
              << " seconds." << std::endl;
}

我使用编译器

icc -O3 -mkl -std=c++11 -DNDEBUG -I/usr/include/eigen3/ test.cpp

并获取输出

#Threads: 1
Elasped time is 0.295782 seconds.

您的 Python 版本在我的机器上报告为 0.399146080017


如果要获得可比较的计时,可以使用 Python 中的 time.clock()(CPU 时间)而不是 time.time()(挂钟时间)。


0

这不是一个公平的比较。Python例程在浮点精度上运作,而C++代码需要处理双精度。这会正好使计算时间加倍。

>>> type(np.random.random_sample())
<type 'float'>

你应该与MatrixXf进行比较,而不是MatrixXd,这样你的MKL代码应该同样快。


它们确实同样快,但是numpy使用的是float64,这与C++中的double相同。 - Wikunia
Numpy也使用openblas和MKL。因此,它无论如何都会使用32位精度进行LU分解。 - Kaveh Vahedipour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接