Python数据结构内存占用行为怪异
我在尝试一个编程珍珠: 给定一个包含最多1000万个7位整数且无重复项的文件。使用1.5Mb RAM按升序打印这些数字并只读取一次数据的有效方法是什么?只有1Mb的RAM而没有其他存储的后果是什么?如果允许重复,您的答案会有什么变化 为了创建一个测试用例I,生成8999999个数字并将它们写入一个文件。 然后,对于每一行,我开始将其插入到树中,最后创建一个trie结构 示例代码:Python数据结构内存占用行为怪异,python,memory-management,heap,programming-pearls,Python,Memory Management,Heap,Programming Pearls,我在尝试一个编程珍珠: 给定一个包含最多1000万个7位整数且无重复项的文件。使用1.5Mb RAM按升序打印这些数字并只读取一次数据的有效方法是什么?只有1Mb的RAM而没有其他存储的后果是什么?如果允许重复,您的答案会有什么变化 为了创建一个测试用例I,生成8999999个数字并将它们写入一个文件。 然后,对于每一行,我开始将其插入到树中,最后创建一个trie结构 示例代码: from sys import getsizeof tree = dict() xtree = dict() f
from sys import getsizeof
tree = dict()
xtree = dict()
f = open("data2.txt", "r")
cnt = 0
for number in f:
cnt += 1
currTree = tree
xtree[number] = dict()
for n in number.strip():
if n not in currTree:
currTree[n] = dict()
currTree = currTree[n]
f.close()
print(cnt)
print(getsizeof(tree))
print(getsizeof(xtree))
print(tree)
示例文件data2.txt有20条记录
生成的树是
现在的问题是,当我对构建的树进行内存大小调整时,在20行时,它显示了240字节的内存足迹
在100行时,树的大小变为368字节
在8999999行中,它还提供368个字节
我构建了一个名为xtree
的辅助映射,它只提供数据
xtree和tree的大小以字节为单位
谁能解释一下这是怎么回事吗?。您的
树只不过是一个最多有10个键值对的dict。在更大的树中,不再有任何键值对。在键值对内的值中有更多的值,但在dict中仍然只有10个键值对。一个大约有10个键值对、占用368字节的dict似乎与您所期望的差不多。1
正如文件所说:
只考虑直接归因于对象的内存消耗,而不考虑它所指对象的内存消耗
有关递归使用getsizeof()
查找容器大小及其所有内容的示例,请参见
由于您实际上没有完全任意的数据结构,只有一个dict of等,并且虽然您有一些共享引用(例如,如果您在内存中已经有一个具有相同值的int时读取数字1234567
,Python将只重用相同的对象),如果您试图验证是否可以容纳1.5MB,那么您确实需要最坏情况下的测量,因此您可能希望跳过已看到值的检查
所以,如果你想的话,你可以写一些简单的东西,而不是用那个食谱。但想法是一样的:
def total_dict_size(d):
size = sys.getsizeof(d)
if isinstance(d, dict):
for key, value in d.items():
size += sys.getsizeof(key) + total_dict_size(value)
return size
另一方面,您的xtree
是一个包含8999999个键值对的dict。做同样的信封背面计算,我希望它会有点低于300MB。相反,它有点超过300MB。够近了
您还将8999999个7位整数存储在堆中。假设有5百万个不同的整数不属于CPython预先创建和缓存的少数小值,就可以得到一些不错的整数。这些整数中的每一个都足够小,可以容纳一个30位数字,因此它们在64位CPython上每个都需要28个字节。因此,如果您在tree
或xtree
上调用上面的递归函数,那么在sys.getsizeof(xtree)
中没有考虑到另外140MB(但事实上,它们被考虑到了,并且给出了最坏的度量实现)
因此,您在树
、xtree
和实际整数之间的总内存使用量可能在750MB左右,这不太符合<1.5MB
的要求
一,。每个Python对象都有一些固定的头开销,比如refcount、指向类型的指针等,加上特定于类型的开销,比如大多数容器类型的长度。称之为64字节。然后,dict有一个哈希表。它需要比10个插槽大一点,以保持负载远低于1.0;叫它13个插槽。每个插槽都需要一个散列值、一个对键的引用和一个对值的引用,因此这是3个指针或24个字节。64 + 13 * 24 = 376. 因此,信封背面的计算只减少了8个字节…您的树只不过是一个最多有10个键值对的dict。在更大的树中,不再有任何键值对。在键值对内的值中有更多的值,但在dict中仍然只有10个键值对。一个大约有10个键值对、占用368字节的dict似乎与您所期望的差不多。1
正如文件所说:
只考虑直接归因于对象的内存消耗,而不考虑它所指对象的内存消耗
有关递归使用getsizeof()
查找容器大小及其所有内容的示例,请参见
由于您实际上没有完全任意的数据结构,只有一个dict of等,并且虽然您有一些共享引用(例如,如果您在内存中已经有一个具有相同值的int时读取数字1234567
,Python将只重用相同的对象),如果您试图验证是否可以容纳1.5MB,那么您确实需要最坏情况下的测量,因此您可能希望跳过已看到值的检查
所以,如果你想的话,你可以写一些简单的东西,而不是用那个食谱。但想法是一样的:
def total_dict_size(d):
size = sys.getsizeof(d)
if isinstance(d, dict):
for key, value in d.items():
size += sys.getsizeof(key) + total_dict_size(value)
return size
另一方面,您的xtree
是一个包含8999999个键值对的dict。做同样的信封背面计算,我希望它会有点低于300MB。相反,它有点超过300MB。够近了
您还将8999999个7位整数存储在堆中。假设有5百万个不同的整数不属于CPython预先创建和缓存的少数小值,就可以得到一些不错的整数。这些整数中的每一个都足够小,可以容纳一个30位数字,因此它们在64位CPython上每个都需要28个字节。因此,sys.getsizeof(xtree)中没有考虑到另外140MB<