numpy.dot比原生C ++ 11慢{100}

时间:2015-06-21 14:41:00

标签: c++ c++11 python-3.x numpy scipy

我有一个Matlab背景,当我一年前买了一台笔记本电脑时,我仔细选择了一台具有很多计算能力的机器,机器有4个线程,它为我提供了8个2.4GHz的线程。机器被证明是非常强大的,并且使用简单的parfor-loops我可以利用所有的处理器线程,为了解决许多问题和实验,我的速度接近8。

这个美好的星期天我正在尝试numpy,人们经常告诉我numpy的核心业务是使用libblas高效实现的,甚至可能使用多个核心和像OpenMP这样的库(使用OpenMP你可以使用c创建类似parfor的循环-style pragma's)。

这是许多数值和机器学习算法的一般方法,您可以使用昂贵的高级操作(如矩阵乘法)来表达它们,但是使用昂贵的高级语言(如Matlab和python)来提高舒适度。而且,c(++)允许我们绕过GIL。

所以很酷的部分是线性代数 - 东西应该在python中处理得非常快,只要你使用numpy。你只需要一些函数调用的开销,但如果它背后的计算很大,那就可以忽略不计了。

所以,在没有触及主题的情况下,不是所有东西都可以用线性代数或其他numpy操作来表达,我给了它一个旋转:

t = time.time(); numpy.dot(range(100000000), range(100000000)); print(time.time() - t)
40.37656021118164

所以我,这40秒我看到机器上8个线程中的一个工作100%,其他线程接近0%。我不喜欢这个,但即使有一个线程正在工作,我希望它能在大约0.something秒内运行。点积产生100M +'es和*'es,所以我们有2400M / 100M =每秒24个时钟滴答,一个+,一个*和任何开销。

然而,对于+,*和开销,算法需要40 * 24 =约= 1000个滴答(!!!!!)。我们在C ++中这样做:

#include<iostream>

int main() {
  unsigned long long result = 0;
  for(unsigned long long i=0; i < 100000000; i++)
    result += i * i;
  std::cout << result << '\n';
}

BLITZ:

herbert@machine:~$ g++ -std=c++11 dot100M.cc 
herbert@machine:~$ time ./a.out
662921401752298880

real    0m0.254s
user    0m0.254s
sys 0m0.000s

0.254秒,几乎比numpy.dot快100倍。

我想,也许python3范围生成器是缓慢的部分,所以我通过首先在std :: vector中存储所有100M数字(使用迭代push_back)而不是迭代它来阻碍我的c ++ 11实现。这个速度要慢得多,它花了不到4秒,仍然快了10倍。

我在ubuntu上使用'pip3 install numpy'安装了我的numpy,它开始编译一段时间,使用gcc和gfortran,而且我看到提到blas-header文件通过编译器输出。

numpy.dot是什么原因那么极慢?

2 个答案:

答案 0 :(得分:12)

所以你的比较是不公平的。在你的python示例中,首先生成两个范围对象,将它们转换为numpy-arrays,然后执行标量产品。计算至关重要。以下是我的电脑号码:

>>> t=time.time();x=numpy.arange(100000000);numpy.dot(x,x);print time.time()-t
1.28280997276

没有数组的生成:

>>> t=time.time();numpy.dot(x,x);print time.time()-t
0.124325990677

完成后,C版本大致需要相同的时间:

real    0m0.108s
user    0m0.100s
sys 0m0.007s

答案 1 :(得分:1)

range根据您给定的参数生成一个列表,其中C中的for循环仅增加一个数字。

我同意花费这么多时间来生成一个列表在计算上显然是相当昂贵的 - 然后再次,这是一个很大的列表,并且你要求其中两个; - )

编辑:正如评论中提到的range生成列表,而不是数组。

尝试使用递增range循环或类似方法替换您的while方法,看看您是否获得了更多可容忍的结果。