numpy 如何高效地并行一个简单的python for循环给数组赋值?

mo49yndu  于 2023-10-19  发布在  Python
关注(0)|答案(1)|浏览(111)

我想并行一个for循环,在每次迭代中为数组赋值。举一个简单的例子:

N = 100000
a = np.zeros(N)
for i in range(N):
    a[i] = i**2 + sum([j**(1/2) for j in range(10**4)])

我知道这不是计算a的有效方法,但这只是为了举例。实际的代码更复杂,但具有类似的结构。而sum([j**(1/2) for j in range(10**4)])只是为了花费一些时间与非并行版本进行比较。
在MALTAB中使用parfor和在Julia中使用Base.Threads很容易。但在Python中似乎并不容易。有什么干净有效的方法吗?非常感谢.

hwazgwia

hwazgwia1#

不,没有简单的方法,你必须使用一些复杂的方法,你已经找到了,而查找这个。CPython有一个全局解释器锁,这将阻止您在Python解释器级别利用并行性。
但你的方法并不理想。
对于初学者来说,你不需要重新计算

sum([j**(1/2) for j in range(10**4)])

在每一次迭代中。如果你只是这样做:

N = 100000
a = np.zeros(N)
x = sum([j**(1/2) for j in range(10**4)])
for i in range(N):
    a[i] = i**2 + x

您的代码将明显更快(可能比简单地添加并行性要快)。
但更根本的是,也许你不应该这样使用numpy,它会很慢,它是为 * 矢量化操作 * 而设计的。
所以你真正想要的只是:

a = (np.arange(N)**2) + np.sqrt(np.arange(10**4)).sum()

所以,为了强调选择正确的算法有多重要,这里是通过这样做你得到了多少改进,在这种情况下,在每次迭代中不需要重新计算,你只需要做一次:

In [1]: import numpy as np

In [2]: %%timeit
   ...: N = 10000
   ...: a = np.zeros(N)
   ...: for i in range(N):
   ...:     a[i] = i**2 + sum([j**(1/2) for j in range(10**4)])
   ...:
9.1 s ± 69.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %%timeit
   ...: N = 10000
   ...: a = np.zeros(N)
   ...: x = sum([j**(1/2) for j in range(10**4)])
   ...: for i in range(N):
   ...:     a[i] = i**2
   ...:
2.06 ms ± 25.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

所以这样做的话会提高几个数量级。
numpy矢量化可以进一步改善这一点:

In [5]: N = 10000

In [6]: %timeit (np.arange(N)**2) + np.sqrt(np.arange(10**4)).sum()
37.3 µs ± 763 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops ea

因此,在尝试使用并行性解决问题之前,首先要确保您的算法是理想的,特别是对于像这样容易实现的问题。然后确保你在使用numpy这样的工具时充分利用了它们的优势。

相关问题