Python中的内存消耗-列表,下标和指针

时间:2019-12-09 18:21:46

标签: python-3.x numpy memory profiling

我试图了解Python对象使用了多少内存。

在以下代码中,我检查了numpy数组vs列表以及带下标的numpy数组的内存:

import sys, os, psutil, numpy as np

def size_of(obj):
    return f'{sys.getsizeof(obj) / 1000000:,.0f} MB'
def get_memory_usage():
    process = psutil.Process(os.getpid())
    return f'{process.memory_info().rss / 1000000:,.0f} MB'

# Numpy vs List
print(f'(1) Mem usage: {get_memory_usage()}')
ONE_HUNDRED_MIL_NP = np.random.randint(-128,127,int(10**8),dtype='int8')
print(f'(2) Mem usage: {get_memory_usage()}, ONE_HUNDRED_MIL_NP: {size_of(ONE_HUNDRED_MIL_NP)}')
ONE_HUNDRED_MIL_LIST = list(np.random.choice(127, int(10**8), replace=True).astype('int8'))
print(f'(3) Mem usage: {get_memory_usage()}, ONE_HUNDRED_MIL_LIST: {size_of(ONE_HUNDRED_MIL_LIST)}')

# Now try subscriping
FOURCOLS = np.random.randint(-128,127,size=(int(10**8),4),dtype='int8')
print(f'(4) Mem usage: {get_memory_usage()}, FOURCOLS: {size_of(FOURCOLS)}')
FOURCOLS_PERMUTED = FOURCOLS[np.random.randint(0,len(FOURCOLS),size=len(FOURCOLS),dtype='int32')]
print(f'(5) Mem usage: {get_memory_usage()}, FOURCOLS_PERMUTED: {size_of(FOURCOLS_PERMUTED)}')

这将返回:

(1) Mem usage: 187 MB
(2) Mem usage: 287 MB, ONE_HUNDRED_MIL_NP: 100 MB
(3) Mem usage: 3,526 MB, ONE_HUNDRED_MIL_LIST: 900 MB
(4) Mem usage: 3,926 MB, FOURCOLS: 400 MB
(5) Mem usage: 4,326 MB, FOURCOLS_PERMUTED: 400 MB

注意:

  1. 输出(2)有意义。 1个int8是8位(1个字节),1亿个字节是100 MB
  2. 输出(3)我不明白:
    1. 第一个问题是sys.getsizeof()显示对象占用900 MB,但是psutil显示该过程现在占用了3,239 MB更多内存(3526-287 = 3239)。幻像内存的使用来自哪里?
    2. 900 MB来自何处? (从Python: Size of Reference?开始,我假设有numpy对象的100 MB加上1亿个指针,每个指针8个字节,所以100 MB + 800 MB = 900 MB?)
  3. 输出(4)有意义。 4亿个int8是400 MB。
  4. 输出(5)我不明白。是否正在制作副本或引用?如果引用,我们仅引用1亿行,对吧?这怎么变成400 MB?

谢谢

1 个答案:

答案 0 :(得分:0)

ONE_HUNDRED_MIL_NP = np.random.randint(-128,127,int(10**8),dtype='int8')

这将创建一个数组。 ONE_HUNDRED_MIL_NP.nbytes是衡量数组大小的好方法。数组具有一些基本信息,例如形状,步幅,dtype,但大部分空间是一个包含字节的1d数据缓冲区,在这种情况下,每个元素为字节。

ONE_HUNDRED_MIL_LIST = list(np.random.choice(127, int(10**8), replace=True).astype('int8'))

这将从该数组生成一个列表。列表具有一个数据缓冲区,其中包含对内存中其他对象的引用。 getsizeof仅测量该缓冲区的大小,而对对象什么也没说。这些对象是numpy.int8对象,它们是从数组中提取的。它们实际上并不引用数组的元素,而是这些值的副本。

从数组中获取列表的更好方法是使用arr.tolist()

FOURCOLS = np.random.randint(-128,127,size=(int(10**8),4),dtype='int8')

这只是另一个数组。 2d形状不会改变占用的内存量。

FOURCOLS_PERMUTED = FOURCOLS[np.random.randint(0,len(FOURCOLS),size=len(FOURCOLS),dtype='int32')]

这是advanced indexing的示例。它使用自己的数据缓冲区(而不是带有共享缓冲区的view)创建一个新数组。是的,您只是索引第一维,但是数据缓冲区存储所有值,而不是对FOURCOLS行的引用。

列表列表确实存储对嵌套列表的引用或指向。因此,对外部列表进行混排只会对引用进行混排。多维c数组还存储引用或指针。但是多维numpy数组使用不同的模型。数据是平面c数组。多维性是由shape/strides迭代代码产生的。


因此,请查看您的电话号码:

(1) Mem usage: 187 MB

基本用法。

(2) Mem usage: 287 MB, ONE_HUNDRED_MIL_NP: 100 MB

为基础增加100mb。

(3) Mem usage: 3,526 MB, ONE_HUNDRED_MIL_LIST: 900 MB

900大约是列表的数据缓冲区使用的内存。总使用量增加的其余部分是用于存储10 ** 8 np.int8个对象。

(4) Mem usage: 3,926 MB, FOURCOLS: 400 MB

这将显示另外400 MB的内存使用情况。

(5) Mem usage: 4,326 MB, FOURCOLS_PERMUTED: 400 MB

还有另外400个。

在没有(3)列表的情况下,创建mem usage应该显示出数组大小的有序增加。