为什么不是numpy.mean多线程?

ggg*_*ggg 21 python performance multithreading numpy

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

我知道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])
Run Code Online (Sandbox Code Playgroud)

aba*_*ert 18

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

谁说它应该是多线程的?

numpy主要设计为在单个内核上尽可能快,并且如果需要,可以尽可能并行化.但你仍然需要并行化它.

特别是,您可以同时操作独立的子对象,并且尽可能慢的操作释放GIL - 尽管"尽可能"可能不够.此外,numpy对象被设计为尽可能容易地在进程之间共享或传递,以便于使用multiprocessing.

有一些自动并行化的专用方法,但大多数核心方法都没有.特别是,dot尽可能在BLAS之上实现,BLAS在大多数平台上自动并行化,但是mean以普通的C代码实现.

有关详细信息,请参阅numpy和scipy的并行编程.


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

对此没有好的答案.您可以进行有根据的猜测(X似乎可能是在ATLAS之上实现的,而我的ATLAS副本是隐式线程的),或者您可以阅读源代码.

但通常,最好的办法是尝试并测试.如果代码使用100%的一个核心和0%的其他核心,请添加手动线程.如果它现在使用100%的一个核心和10%的其他核心并且几乎没有更快地运行,那么将多线程更改为多处理.(幸运的是,Python使这非常简单,特别是如果您使用来自concurrent.futures或来自池类的Executor类multiprocessing.但是您仍然经常需要考虑它,并测试共享与传递的相对成本,如果您有大型数组.)

另外,正如kwatford所指出的那样,仅仅是因为某些方法似乎并不是隐式并行并不意味着它不会在numpy的下一个版本或BLAS的下一个版本中或在不同的平台上并行,或者甚至在装有略微不同的东西的机器上也是如此.所以,准备重新测试.并做一些类似的事情my_mean = numpy.mean然后my_mean到处使用,所以你可以改变一行my_mean = pool_threaded_mean.


dr *_*bob 8

基本上,因为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,则上述所有三个将自动并行运行.当然,实际上人们希望有一些运行时控制 - 例如,如果计划在同一台多处理器计算机上运行多个作业,可能需要关闭自动并行化.

  • +1。唯一的问题是它们不能保证在任何地方哪些操作会释放 GIL 和/或在部分子对象上独立操作,只是它们试图使一切都可以线程化。因此,您几乎必须测试手动线程,交叉手指,并准备好在必要时退回到多处理(并且,对于足够大的数组,您还必须测试共享与传递 IPC)。幸运的是,Python 在必要时可以很容易地回退到多处理,所以这不是什么大问题。 (3认同)