Python 为什么不是';是指多线程?
自从我注意到numpy只使用了一个内核以来,我一直在寻找方法来轻松地多线程处理我的一些简单分析代码,尽管事实上它应该是多线程的 我知道numpy是为多核配置的,因为我可以看到使用numpy的测试。dot使用我所有的核,所以我只是将mean重新实现为dot产品,它运行得更快。是不是有什么原因让我们不能自己跑这么快?对于较大的阵列,我发现了类似的行为,尽管这个比率比我的示例中显示的3接近2 我一直在读一些关于类似numpy速度问题的帖子,显然它的方式比我想象的要复杂得多。任何见解都会有帮助,我更喜欢使用mean,因为它可读性更强,代码更少,但我可能会切换到基于点的方法Python 为什么不是';是指多线程?,python,multithreading,performance,numpy,Python,Multithreading,Performance,Numpy,自从我注意到numpy只使用了一个内核以来,我一直在寻找方法来轻松地多线程处理我的一些简单分析代码,尽管事实上它应该是多线程的 我知道numpy是为多核配置的,因为我可以看到使用numpy的测试。dot使用我所有的核,所以我只是将mean重新实现为dot产品,它运行得更快。是不是有什么原因让我们不能自己跑这么快?对于较大的阵列,我发现了类似的行为,尽管这个比率比我的示例中显示的3接近2 我一直在读一些关于类似numpy速度问题的帖子,显然它的方式比我想象的要复杂得多。任何见解都会有帮助,我更喜欢
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])
基本上,因为BLAS库有一个优化的点积,他们可以很容易地调用本质上是并行的
dot
。他们承认他们可以扩展numpy以并行其他操作,但选择不走这条路。但是,他们给出了一些关于如何并行化numpy代码的提示(基本上是在N个核之间划分工作(例如,N=4),将数组拆分为N个子数组,并将每个子数组的作业发送到其自己的线程,然后合并结果)
见:
使用并行原语
numpy的一大优点是可以非常清晰地表达数组操作。例如,要计算矩阵A和矩阵B的乘积,只需执行以下操作:
>C=numpy.dot(A,B)
不仅读写简单明了,因为numpy知道你想做一个矩阵点积,所以它可以使用作为“BLAS”(基本线性代数子程序)一部分的优化实现。这通常是一个经过仔细调整的库,通过利用缓存内存和汇编程序实现在硬件上尽可能快地运行。但是现在很多架构都有一个BLAS,它也利用了多核机器。如果您的numpy/scipy是使用其中一个进行编译的,那么dot()将在不执行任何操作的情况下并行计算(如果速度更快的话)。类似于其他矩阵运算,如反演、奇异值分解、行列式等。例如,开源库ATLAS允许在编译时选择并行级别(线程数)。Intel专有的MKL库提供了在运行时选择并行级别的可能性。还有GOTO库,允许运行时选择并行级别。这是一个商业产品,但源代码免费分发供学术使用
最后,scipy/numpy不会并行化像
>A=B+C
>A=numpy.sin(B)
>A=scipy.stats.norm.isf(B)
这些操作按顺序运行,不利用多核机器(但请参见下文)。原则上,这可以在不做太多工作的情况下改变。OpenMP是C语言的一个扩展,它允许编译器为适当注释的循环(和其他东西)生成并行化代码。如果有人坐下来在numpy(可能还有scipy)中注释几个核心循环,然后有人在打开OpenMP的情况下编译numpy/scipy,上述三个循环将自动并行运行。当然,在现实中,人们可能想要一些运行时控制——例如,如果一个人计划在同一台多处理器机器上运行多个作业,那么他可能想要关闭自动并行化
自从我注意到numpy只使用了一个内核以来,我一直在寻找方法来轻松地多线程处理我的一些简单分析代码,尽管事实上它应该是多线程的
谁说它应该是多线程的
numpy
主要设计为在单个内核上尽可能快,并且在需要时尽可能并行。但你还是要把它并行化
特别是,您可以同时对独立的子对象进行操作,如果可能,慢速操作会释放GIL,尽管“如果可能”可能还不够。另外,numpy
对象被设计为尽可能容易地在进程之间共享或传递,以便于使用多处理
有一些专门的方法是自动并行的,但大多数核心方法不是。特别是,dot
在可能的情况下在BLAS之上实现,BLAS在大多数平台上自动并行,但mean
在普通C代码中实现
有关详细信息,请参阅
那么,您如何知道哪些方法是并行的,哪些不是?还有,在那些没有的情况下,您如何知道哪些可以很好地手动线程化,哪些需要多处理 没有好的答案。您可以进行有根据的猜测(X看起来可能是在ATLAS上实现的,我的ATLAS副本是隐式线程的),或者您可以阅读源代码 但通常情况下,最好的做法是尝试和测试。如果代码使用一个内核的100%和其他内核的0%,请添加手动线程。如果它现在使用100%的一个内核和10%的其他内核,并且运行速度几乎没有提高,那么将多线程更改为多处理。(幸运的是,Python使这变得非常容易,特别是如果您使用
concurrent.futures
中的Executor类或multiprocessing
中的Pool类。但是您仍然需要经常考虑一下,如果您有大型数组,那么测试共享与传递的相对成本。)
此外,正如kwatford指出的,仅仅因为某些方法似乎不是隐式并行的,并不意味着它在下一版本的numpy或下一版本的