2013-05-17 134 views
11

我一直在寻找方法来轻松多线程的一些简单的分析代码,因为我注意到numpy它只使用一个核心,尽管事实上它应该是多线程的。为什么不是numpy.mean多线程?

我知道numpy被配置为多核心,因为我可以看到使用numpy.dot的测试使用了所有的核心,所以我只是将其重新实现为点积,并且运行速度更快。是否有某种原因意味着它不能单独运行?我发现对于较大的阵列也有类似的行为,虽然比例接近2比我的例子中显示的3。

我一直在阅读一堆类似numpy速度问题的帖子,显然它的方式比我想象的要复杂。任何见解都会有所帮助,因为它更具可读性并且代码更少,所以我宁愿使用mean,但我可能会切换到基于点的方式。

In [27]: data = numpy.random.rand(10,10) 

In [28]: a = numpy.ones(10) 

In [29]: %timeit numpy.dot(data,a)/10.0 
100000 loops, best of 3: 4.8 us per loop 

In [30]: %timeit numpy.mean(data,axis=1) 
100000 loops, best of 3: 14.8 us per loop 

In [31]: numpy.dot(data,a)/10.0 - numpy.mean(data,axis=1) 
Out[31]: 
array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 
     0.00000000e+00, 1.11022302e-16, 0.00000000e+00, 
     0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 
     -1.11022302e-16]) 
+0

我想这是因为numpy是用python编写的,而python中的多线程因为GIL而效率很低。 –

+3

@Riateche numpy的核心是用C编写的。 – agf

+4

一定要做一个叫做'mean'的函数来使用,而不是在任何地方使用点的东西,所以如果'numpy.mean'改进,你可以稍后改变它。 – kwatford

回答

12

我一直在寻找各种方法来容易多线程我的一些简单的分析代码,因为我numpy的它只是使用一个核心,尽管事实上,它应该是多线程已经注意到。

谁说它应该是多线程?

numpy主要是为了尽可能快地在单独的核心,如果你需要的话,以尽可能并行。但是你仍然需要并行化。

特别的,你可以在同一时间独立的子对象进行操作,而缓慢的操作释放GIL时可能的,虽然“可能的情况下”未必是远远不够的。此外,numpy对象旨在尽可能方便地在流程之间共享或传递,以便于使用multiprocessing

但是也有一些自动并行化一些专门的方法,但是大多数的核心方法都没有。特别是,dot在可能的情况下在BLAS之上实现,BLAS在大多数平台上自动并行化,但mean以纯C代码实现。

详见Parallel Programming with numpy and scipy


那么,你怎么知道哪些方法是并行的,哪些不是?而且,在那些不是,你怎么知道哪些可以很好地手动线程,哪些需要多处理?

没有很好的答案。你可以做出有根据的猜测(X看起来好像可能是在ATLAS之上实现的,我的ATLAS副本是隐含地线程化的),或者你可以阅读源代码。

但通常情况下,最好的做法是尝试并测试。如果代码使用一个核心的100%和其他的0%,则添加手动线程。如果现在使用一个核心的100%和其他10%的核心,并且几乎没有更快的运行速度,请将多线程更改为多处理。 (幸运的是,Python使得这很容易,尤其是如果你使用concurrent.futures的Executor类或者multiprocessing的Pool类。但是你仍然经常需要考虑一下,并且测试共享与传递的相对成本,如果你有大阵列)。

此外,正如kwatford指出的那样,仅仅因为某些方法似乎并不隐含并行并不意味着它不会在下一版本的numpy或下一版本的BLAS中并行,或者在不同的平台上,甚至在安装了稍微不同的东西的机器上。所以,准备重新测试。并执行诸如my_mean = numpy.mean之类的操作,然后随处使用my_mean,因此您只需将一行更改为my_mean = pool_threaded_mean即可。

6

基本上,因为BLAS库具有优化的点积,他们可以很容易地要求dot即本质为并行。他们承认他们可以延伸numpy来平行其他行动,但选择不走这条路线。然而,他们给出了关于如何并行化你的numpy代码的几个技巧(基本上是为了在N个核心之间划分工作(例如,N = 4),将你的阵列分成N个子阵列并且将每个子阵列的作业发送到它自己的线程,然后结合你的结果)。

http://wiki.scipy.org/ParallelProgramming

使用并行原语

一个numpy的的巨大优势是,你可以表达阵列操作非常干净。例如,要计算矩阵A的产品和矩阵B,你只是做:

>>> C = numpy.dot(A,B)

这不仅是简单明了的读写,因为numpy的知道你想要做一个矩阵点产品,它可以使用作为“BLAS”(基本线性代数子程序)的一部分获得的优化实现。这通常是一个经过仔细调整的库,可以利用缓存和汇编实现在硬件上尽可能快地运行。但是现在许多架构都有一个BLAS,它也可以利用多核机器。如果你的numpy/scipy是使用其中一种编译的,那么dot()将被并行计算(如果这更快),而你没有做任何事情。对于其他矩阵运算也是如此,如反演,奇异值分解,行列式等等。例如,开源库ATLAS允许编译时选择并行度(线程数)。来自英特尔的专有MKL库提供了在运行时选择并行度的可能性。还有GOTO库允许运行时选择并行性水平。这是一个商业产品,但源代码免费分发给学术用途。

最后,SciPy的/ numpy的不并行操作,如

>>> A = B + C

>>> A = numpy.sin(B)

>>> A = scipy.stats.norm.isf(B)

这些操作顺序运行,不采取任何多核机器的优势(见下文)。原则上,这可以在没有太多工作的情况下改变。 OpenMP是C语言的扩展,它允许编译器为适当注释的循环(和其他事物)生成并行代码。如果有人在numpy(可能是scipy)中坐下并注释了一些核心循环,并且如果有人在打开OpenMP的情况下编译了numpy/scipy,则上述三项都将自动并行运行。当然,实际上人们会希望有一些运行时控制 - 例如,如果有人计划在同一个多处理器机器上运行多个作业,可能需要关闭自动并行化。

+0

+ 1。唯一的问题是,他们不能保证任何操作释放GIL和/或在部分子对象上独立操作,只是为了让所有东西都可以线程化。所以你几乎必须测试手动线程,跨越你的手指,并准备在必要时回退到多处理(并且,对于一个足够大的阵列,你还必须测试共享与传递给IPC)。幸运的是,Python在需要时很容易回退到多处理,所以这不是什么大问题。 – abarnert

相关问题