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

Does NumPy array really take less memory than python list?

请参考下面执行-

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 个字节。这个 space 是否用于指向关联的内置方法和属性?

计算使用:

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

在上面的link中,可以读出ndarray.nbytes:

Does not include memory consumed by non-element attributes of the array object.

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

中的值

可以在 Array Attributes 部分找到非元素属性的列表,为了完整起见,在此处包括:

ndarray.flags Information about the memory layout of the array.
ndarray.shape Tuple of array dimensions.
ndarray.strides Tuple of bytes to step in each dimension when traversing an array.
ndarray.ndim Number of array dimensions.
ndarray.data Python buffer object pointing to the start of the array’s data.
ndarray.size Number of elements in the array.
ndarray.itemsize Length of one array element in bytes.
ndarray.nbytes Total bytes consumed by the elements of the array.
ndarray.base Base object if memory is from some other object.

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

Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to.

因为 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

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

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

基本要点是:

  1. 列表是一个容器,getsizeof文档警告我们它returns只是容器的大小,而不是它引用的元素的大小。因此,就其本身而言,它是衡量列表(或元组或字典)总大小的不可靠指标。

  2. getsizeof 是一个相当好的数组度量,如果你考虑到大约 100 字节的“开销”。这种开销对于小数组来说是很大的一部分,而对于大数组来说则是次要的。 nbytes是比较简单的数组内存占用判断方式

  3. 但是对于views,data-buffer是和base共享的,在使用getsizeof.

    时不算数
  4. object dtype 数组包含像列表一样的引用,同样 getsizeof 警告适用。

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