Python 内存中numpy跨步数组/广播数组的大小?

Python 内存中numpy跨步数组/广播数组的大小?,python,numpy,Python,Numpy,我试图用numpy创建高效的广播数组,例如一组shape=[10001000]数组,它们只有1000个元素,但重复了1e6次。这可以通过np.lib.stride\u技巧实现。as\u stride和np.broadcast\u数组 但是,我在验证内存中是否没有复制时遇到了问题,这一点非常关键,因为实际复制内存中阵列的测试往往会使我的机器崩溃,而不会留下任何回溯 我尝试使用.nbytes检查数组的大小,但这似乎与实际内存使用情况不符: >>> import numpy as n

我试图用numpy创建高效的广播数组,例如一组
shape=[10001000]
数组,它们只有1000个元素,但重复了1e6次。这可以通过
np.lib.stride\u技巧实现。as\u stride
np.broadcast\u数组

但是,我在验证内存中是否没有复制时遇到了问题,这一点非常关键,因为实际复制内存中阵列的测试往往会使我的机器崩溃,而不会留下任何回溯

我尝试使用
.nbytes
检查数组的大小,但这似乎与实际内存使用情况不符:

>>> import numpy as np
>>> import resource
>>> initial_memuse = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
>>> pagesize = resource.getpagesize()
>>>
>>> x = np.arange(1000)
>>> memuse_x = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
>>> print("Size of x = {0} MB".format(x.nbytes/1e6))
Size of x = 0.008 MB
>>> print("Memory used = {0} MB".format((memuse_x-initial_memuse)*resource.getpagesize()/1e6))
Memory used = 150.994944 MB
>>>
>>> y = np.lib.stride_tricks.as_strided(x, [1000,10,10], strides=x.strides + (0, 0))
>>> memuse_y = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
>>> print("Size of y = {0} MB".format(y.nbytes/1e6))
Size of y = 0.8 MB
>>> print("Memory used = {0} MB".format((memuse_y-memuse_x)*resource.getpagesize()/1e6))
Memory used = 201.326592 MB
>>>
>>> z = np.lib.stride_tricks.as_strided(x, [1000,100,100], strides=x.strides + (0, 0))
>>> memuse_z = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
>>> print("Size of z = {0} MB".format(z.nbytes/1e6))
Size of z = 80.0 MB
>>> print("Memory used = {0} MB".format((memuse_z-memuse_y)*resource.getpagesize()/1e6))
Memory used = 0.0 MB
因此,
.nbytes
报告数组的“理论”大小,但显然不是实际大小。
resource
检查有点笨拙,因为看起来有一些东西正在加载和缓存(可能?),这会导致第一次跨步占用一些内存,但未来的跨步不会占用任何内存


tl;dr:如何确定内存中numpy数组或数组视图的实际大小?

一种方法是检查数组的大小,它引用数组“借用”内存的对象。例如:

x = np.arange(1000)
print(x.flags.owndata)      # x "owns" its data
# True
print(x.base is None)       # its base is therefore 'None'
# True

a = x.reshape(100, 10)      # a is a reshaped view onto x
print(a.flags.owndata)      # it therefore "borrows" its data
# False
print(a.base is x)          # its .base is x
# True
使用
np.lib.stride\u技巧,事情会稍微复杂一些:

b = np.lib.stride_tricks.as_strided(x, [1000,100,100], strides=x.strides + (0, 0))

print(b.flags.owndata)
# False
print(b.base)   
# <numpy.lib.stride_tricks.DummyArray object at 0x7fb40c02b0f0>
因此,我们可以检查
b.base.base

print(b.base.base is x)
# True
拥有基本数组后,其
.nbytes
属性应准确反映其占用的内存量

原则上,可以拥有阵列视图,也可以从另一个跨步阵列创建跨步阵列。假设您的视图或跨步数组最终由另一个numpy数组支持,您可以递归地引用它的
.base
属性。找到
.base
None
的对象后,就找到了数组借用其内存的基础对象:

def find_base_nbytes(obj):
    if obj.base is not None:
        return find_base_nbytes(obj.base)
    return obj.nbytes
果然,

print(find_base_nbytes(x))
# 8000

print(find_base_nbytes(y))
# 8000

print(find_base_nbytes(z))
# 8000

简单地查找Python进程的驻留集大小并不是确定特定numpy数组使用了多少内存的可靠方法。它不考虑分页,也没有简单的方法来预测分配给已删除或超出范围数组的内存何时会释放回操作系统。
print(find_base_nbytes(x))
# 8000

print(find_base_nbytes(y))
# 8000

print(find_base_nbytes(z))
# 8000