NumPy数组真的比Python列表占用更少的内存吗?

8
请参考以下执行 -
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列表,但另一种计算方式表明其消耗的内存比Python列表更多?我是否应该使用getSizeOf来检查numpy数组。我在这里做错了什么?

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


2
getsize在列表中几乎没有用处。在担心getsize显示的内容之前,请测试一个大数组(1000个项目)的大小。 - hpaulj
3个回答

7

使用以下计算方法:

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,在文档中可以看到(重点是我的):
只计算 直接归因于对象的内存消耗,而不是它引用的对象的内存消耗。

4

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

基本要点如下:

  1. 列表是一个容器,getsizeof文档警告我们它仅返回容器的大小,而不是它所引用元素的大小。因此,仅凭它本身作为列表(或元组或字典)总大小的度量是不可靠的。

  2. getsizeof 是衡量数组相当不错的方法,如果考虑到大约100字节的“开销”。 对于小的数组而言,这个开销将占据很大一部分,而对于大的数组则只是次要的。 使用 nbytes 是判断数组内存使用的更简单方法。

  3. 但对于视图而言,数据缓冲区与基础共享,使用 getsizeof 时不计入其中。

  4. 对象dtype数组包含像列表一样的引用,因此同样适用于 getsizeof 的注意事项。

总体来说,我认为理解数组和列表的存储方式是判断它们各自存储器使用率的更有用方法。更关注计算效率而非内存使用。 对于小型和迭代使用,列表更好。 当数组较大且使用数组方法进行计算时,数组最佳。


1
因为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电脑上遇到了以下问题:

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

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


这个答案和问题一样,仍然没有考虑列表中元素的大小。sys.getsizeof不会递归迭代它们。由于所有元素都是完整的Python int对象,因此列表的大小将大一到两个数量级,并且这应该是重点。 - jsbueno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接