【问题标题】:Numpy: Memory allocations on each operation?Numpy:每个操作的内存分配?
【发布时间】:2015-11-04 17:55:54
【问题描述】:

numpy 是否为您在矩阵上执行的每个操作分配新矩阵?

例如:

A = np.random.rand(10, 20)
A = 2 * A  # Operation 1: Is a copy of A made, and a reference assigned to A?
B = 2 * A  # Operation 2: Does B get a completely different copy of A?
C = A      # Operation 3: Does C get a reference to A?

和切片操作:

A[0, :] = 3

链式操作怎么样?

D = A * B * C  # Elementwise multiplication, if A * B allocates memory, does 
               # (A * B) * C allocate another patch of memory?

Numpy 是一个很棒的库,但我只想知道幕后发生了什么。我的直觉说切片操作会修改内存视图,但我不知道分配。

【问题讨论】:

  • 您是担心速度,还是担心使用非常大的数组时内存不足?
  • @hpaulj:我主要关心的是速度。但记忆是我要注意的另一件事。

标签: python numpy


【解决方案1】:

请记住,numpy 数组是 Python 对象。 Python 不断地创建和删除对象。该数组具有在.FLAGS 和.__array_interface__ 字典中显示的属性,例如shape 和dtype。占用(可能)大量内存的属性是数据缓冲区。它可能是几个字节长,也可能是 MB。

在可能的情况下,numpy 操作会尽量避免复制数据缓冲区。索引时,如果可能,它将返回view。我认为文档在比较视图和副本方面做得很好。

但视图不同于 Python 引用。共享引用意味着两个变量(或列表或字典中的指针)指向同一个 Python 对象。 view 是一个不同的数组对象,但它与另一个数组共享数据缓冲区。副本有自己的数据缓冲区。

在你的例子中:

A = np.random.rand(10, 20)

A 是一个指向数组对象的变量。该对象有一个包含 200 个浮点数(200*8 字节)的数据缓冲区。

A = 2 * A  # Operation 1: Is a copy of A made, and a reference assigned to A?

2*A 创建一个带有新数据缓冲区的新对象。它的任何数据值都不能与原始A 共享。 A=... 重新分配 A 变量。旧的A 对象“丢失”,最终内存被垃圾回收。

B = 2 * A  # Operation 2: Does B get a completely different copy of A?

这个2*A 在新的A 数组上运行。该对象分配给B。 A 保持不变。

C = A      # Operation 3: Does C get a reference to A?

是的,这只是普通的 Python 分配。 C 与 A 指代相同的对象。 id(C)==id(A).

B = A[1,:]  #  B is a view

B 是对新数组对象的引用。但该对象与A 共享数据缓冲区。这是因为只需从不同的点开始并使用不同的shape,就可以在缓冲区中找到所需的值。

A[0, :] = 3

此 LHS 切片将更改 A 值的子集。它类似于:

B = A[0, :]
B = 3

但是 LHS 和 RHS 切片之间存在细微差别。在 LHS 上,您必须更加注意何时获得副本而不是视图。我在A[idx1,:][:,idx2] = 3 之类的表达式中看到了这一点。

D = A * B * C 

在这样的计算中生成了多少中间副本的详细信息隐藏在 numpy C 代码中。假设它执行以下操作是最安全的:

temp1 = A*B
temp2 = temp1*C
D = temp2
(temp1 goes to garbage)

对于普通计算,不必担心这些细节。如果你真的在追求速度,你可以在替代品上做一个timeit。偶尔我们会收到有关操作的问题,请提供memory errors。进行搜索以获取有关这些内容的更多详细信息。

【讨论】:

    【解决方案2】:

    是的,它会创建新数组。除了 C。C 和 A 指向同一个内存。

    您可以自己测试所有这些。尝试id(A) 命令查看内存A 指向的位置。此外,只需创建一个较小的结构并修改其中的一部分,然后查看 A、B 和/或 C 是​​否也更新了。

    【讨论】:

    • 很好的答案,谢谢。我添加了一个回复来询问有关链式操作的问题。你知道 numpy 是如何评估这些的吗?
    • 我不太清楚,但是,一般来说,python 将 D = A * B * C 解释为 D = A.__mul__(B).__mul__(C) 所以我假设有一个为 A * B 计算的中间结果,然后再次为该结果计算* 最后分配给 D 的 C。如果您对此感到担心,可以查看 numpy 的源代码。
    猜你喜欢
    • 2016-08-06
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 2018-02-04
    • 2021-10-16
    • 1970-01-01
    相关资源
    最近更新 更多