【问题标题】:Does NumPy array really take less memory than python list?NumPy 数组真的比 python 列表占用更少的内存吗?
【发布时间】:2022-01-05 00:31:12
【问题描述】:

请参考下面的执行-

import sys

_list = [2,55,87]
print(f'1 - Memory used by Python List - {sys.getsizeof(_list)}')
      
narray = np.array([2,55,87])
size = narray.size * narray.itemsize
print(f'2 - Memory usage of np array using itemsize  - {size}')
print(f'3 - Memory usage of np array using getsizeof  - {sys.getsizeof(narray)}')

这是我得到的结果

1 - Memory used by Python List - 80
2 - Memory usage of np array using itemsize  - 12
3 - Memory usage of np array using getsizeof  - 116

一种计算方式表明 numpy 数组消耗的内存太少,但另一种计算方式说它比常规 python 列表消耗更多?我不应该将 getSizeOf 与 numpy 数组一起使用。我在这里做错了什么?

编辑 - 我刚刚检查过,一个空的 Python 列表占用 56 个字节,而一个空的 np 数组占用 104 个字节。这个空间是否用于指向相关的内置方法和属性?

【问题讨论】:

  • getsize 对于列表几乎没用。并在您担心 getsize 向您显示什么之前测试一个大数组(1000 个项目)的大小。

标签: python numpy memory-management


【解决方案1】:

计算使用:

size = narray.size * narray.itemsize

不包括数组对象的非元素属性所消耗的内存。这可以通过ndarray.nbytes的文档来验证:

>>> x = np.zeros((3,5,2), dtype=np.complex128)
>>> x.nbytes
480
>>> np.prod(x.shape) * x.itemsize
480

在上面的链接中,可以读到ndarray.nbytes

不包括非元素属性消耗的内存 数组对象。

请注意,从上面的代码中,您可以得出结论,您的计算排除了非元素属性,因为该值等于 ndarray.nbytes 中的值。

可以在Array Attributes 部分中找到非元素属性的列表,包括此处以确保完整性:

ndarray.flags 有关数组内存布局的信息。
ndarray.shape 数组维度的元组。
ndarray.strides 元组 遍历数组时要在每个维度中步进的字节数。
ndarray.ndim 数组维度数。
ndarray.data 指向开头的 Python 缓冲区对象数组的数据。
ndarray.size 数组中的元素数。
ndarray.itemsize 一个数组元素的长度(以字节为单位)。
ndarray.nbytes 数组元素消耗的总字节数。
ndarray.base 如果内存来自其他对象,则为基对象。

关于sys.getsizeof,可以在文档(强调我的)中读到:

只有直接归因于对象的内存消耗是 占,而不是它所引用的对象的内存消耗。

【讨论】:

    【解决方案2】:

    [numpy]getsizeof 上搜索会产生许多潜在的重复项。

    基本点是:

    1. 列表是一个容器,getsizeof docs 警告我们它只返回容器的大小,而不是它引用的元素的大小。所以它本身是一个列表(或元组或字典)总大小的不可靠度量。

    2. getsizeof 是一个相当好的数组度量,如果您考虑到大约 100 字节的“开销”。这种开销将是一个小数组的很大一部分,而在查看一个大数组时则是一件小事。 nbytes是判断数组内存使用的比较简单的方法。

    3. 但是对于views,data-buffer 是与base 共享的,使用getsizeof 时不算。

    4. object dtype 数组包含类似列表的引用,同样getsizeof 注意适用。

    总的来说,我认为了解数组和列表的存储方式是判断它们各自内存使用的更有用的方法。更多地关注计算效率而不是内存使用。对于小东西和迭代使用,列表更好。数组在较大时是最好的,并且您使用数组方法进行计算。

    【讨论】:

      【解决方案3】:

      因为numpy 数组具有定义数据布局的形状、步幅和其他成员变量,所以(可能)需要一些额外的内存是合理的!

      另一方面,list 没有特定的类型或形状等。

      虽然,如果您开始在列表中添加元素,而不是简单地将它们写为数组,并且还会转到更多的元素,例如1e7,你会看到不同的行为!

      示例案例:

      import numpy as np
      import sys
      
      N = int(1e7)
      
      narray = np.zeros(N);
      mylist = []
      
      for i in range(N):
          mylist.append(narray[i])
      
      print("size of np.array:", sys.getsizeof(narray))
      print("size of list    :", sys.getsizeof(mylist))
      

      在我的 (ASUS) Ubuntu 20.04 PC 上,我得到:

      size of np.array: 80000104
      size of list    : 81528048
      

      注意,不仅内存占用对应用程序的效率很重要!数据布局有时更为重要。

      【讨论】:

      • 这个答案和问题一样,仍然不考虑列表中元素的大小。 sys.getsizeof 不会对这些进行递归迭代。列表会大一到两个数量级,因为所有元素都是完整的 Python int 对象,这应该是重点
      猜你喜欢
      • 2021-12-22
      • 2012-08-19
      • 2023-03-20
      • 2023-04-09
      • 2018-10-04
      • 1970-01-01
      • 2018-03-21
      • 1970-01-01
      • 2012-08-03
      相关资源
      最近更新 更多