gpt4 book ai didi

python - numpy是否正在重用未使用的数组中的内存?

转载 作者:行者123 更新时间:2023-12-03 16:23:11 28 4
gpt4 key购买 nike

在使用Python和Numpy实现某些Gauss-Seidel求解器时,我发现了一个有趣的副作用。
我试图提取一些最小的例子:


number = 1000
startup = 'import numpy as np;N = 2048;X = np.arange(N * N).reshape((N, N));'
startup2 = startup + 'np.empty_like(X)'


example = ('X[1:: 2, : -1: 2] = ('
'X[: -1: 2, 1:: 2] +'
'X[1:: 2, : -1: 2] +'
'X[1:: 2, : -1: 2] +'
'X[: -1: 2, : -1: 2])/4')
在我的机器上运行 print(timeit.timeit(example, setup=startup, number=number))大约需要5秒钟
print(timeit.timeit(example, setup=startup2, number=number))则需要约4秒钟的时间。
因此,尽管 np.emtpy_like(X)有这种不必要的数组分配,但速度快了大约1s。我在各种机器上以及各种数组大小或迭代中观察到了这种影响。
我假设分配中右手边的计算会导致时间上的数组分配。
似乎Numpy以某种方式重用了用 np.emtpy_like(X)创建的未使用的数组,以加快临时数组的分配。
我对这个假设是正确的,还是时间差异的原因完全不同?
如果我删除 /4
 example = ('X[1:: 2, : -1: 2] = ('
'X[: -1: 2, 1:: 2] +'
'X[1:: 2, : -1: 2] +'
'X[1:: 2, : -1: 2] +'
'X[: -1: 2, : -1: 2])')
然后,我看不到不同版本之间执行时间的差异。因此,我假设在这种情况下,计算可以就地完成,然后就没有时间分配。
有没有更明确的方法可以利用这种效果?对我来说,仅仅编写np.emtpy_like(X)`看起来有些“hacky”。
提前致谢!
更新:
到目前为止,感谢您的回答和评论。
最终,我找到了更多时间来弄乱我的观察结果,并且不确定我上面的解释是否足够清楚。
所以我最初的观察是
    number = 1000
N = 1024
X = np.arange(N * N).reshape((N, N))
np.empty_like(X)
for _ in range(number):
X[1:: 2, : -1: 2] = (X[: -1: 2, 1:: 2] + X[1:: 2, : -1: 2] +
X[1:: 2, : -1: 2] + X[: -1: 2, : -1: 2]) / 4
比这快
    number = 1000
N = 1024
X = np.arange(N * N).reshape((N, N))
for _ in range(number):
X[1:: 2, : -1: 2] = (X[: -1: 2, 1:: 2] + X[1:: 2, : -1: 2] +
X[1:: 2, : -1: 2] + X[: -1: 2, : -1: 2]) / 4
我感到非常惊讶,因为使用 np.empty_like(X)进行的完全未使用和不必要的数组分配似乎加速了下面的循环。
因此,只要分配了未使用的数组,是否使用 np.empty_likezeros_likeones_likeones(X.shape)X.copy()都没关系。
对于不同的N,不同的迭代次数以及在不同的机器上,也会发生这种情况。
此外,我尝试调查 tracemalloc软件包的问题:
   number = 1000
N = 1024
X = np.arange(N * N).reshape((N, N))
tracemalloc.start()
np.empty_like(X)
for _ in range(number):
X[1:: 2, : -1: 2] = (X[: -1: 2, 1:: 2] + X[1:: 2, : -1: 2] +
X[1:: 2, : -1: 2] + X[: -1: 2, : -1: 2]) / 4
s2 = tracemalloc.take_snapshot()

display_top(s2)
其中display_top是文档中的方法,除了我以字节为单位打印而不是以KB为单位打印。
当我在没有 np.empty_like(X)的额外数组分配的情况下运行它时,我得到一些输出,如下所示:
Top 10 lines
#1: ./minexample.py:40: 160.0 B
X[1:: 2, : -1: 2] = (X[: -1: 2, 1:: 2] + X[1:: 2, : -1: 2] + X[1:: 2, : -1: 2] + X[: -1: 2, : -1: 2]) / 4
#2: ./minexample.py:39: 28.0 B
for _ in range(1000):
Total allocated size: 188.0 B
有了额外的分配,我得到了:
Top 10 lines
#1: ./minexample.py:40: 128.0 B
X[1:: 2, : -1: 2] = (X[: -1: 2, 1:: 2] + X[1:: 2, : -1: 2] + X[1:: 2, : -1: 2] + X[: -1: 2, : -1: 2]) / 4
#2: ./minexample.py:38: 32.0 B
np.empty_like(X)
#3: ./minexample.py:39: 28.0 B
for _ in range(1000):
Total allocated size: 188.0 B
因此,与没有预分配的未使用数组时相比,循环中分配的行的大小要小。因此在我看来,这个未使用的数组已被重用。
也许这可以解释这种影响?

最佳答案

empty_like(X)初始化一个具有与X相同尺寸的数组(或矩阵),并用random values填充它,这是一个非常快的过程。但是请记住,+ empty_like(X)不能给您想要的结果(您需要zeros_like(X))!
为什么在numpy中快速添加两个数组与密集排列的数组有关,并在此进行了描述:https://stackoverflow.com/a/8385658/14344821,这可能比用显式提及的条目创建矩阵X更快。
here提供了有关如何有效创建numpy数组的技巧。

关于python - numpy是否正在重用未使用的数组中的内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64481496/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com