Dra*_*nis 5 python memory-management tuples frozenset
我有一个需要用 0-3 个字符串“标记”的对象(一组 20 个可能的字符串);这些值都是唯一的,顺序无关紧要。唯一需要对标签进行的操作是检查特定标签是否存在 ( specific_value in self.tags)。
但是,内存中同时存在大量这些对象,以至于它超出了我旧计算机 RAM 的极限。所以节省几个字节可以加起来。
每个对象上的标签很少,我怀疑查找时间会很重要。但是:在这里使用元组和frozenset 之间是否存在内存差异?是否有任何其他真正的理由使用一个而不是另一个?
元组非常紧凑。集合基于哈希表,并依赖于具有“空”槽来减少哈希冲突的可能性。
对于足够新的 CPython 版本,sys._debugmallocstats()显示许多潜在的有趣信息。在 64 位 Python 3.7.3 下:
>>> from sys import _debugmallocstats as d
>>> tups = [tuple("abc") for i in range(1000000)]
Run Code Online (Sandbox Code Playgroud)
tuple("abc")创建一个包含 3 个 1 个字符的字符串的元组,('a', 'b', 'c'). 在这里,我将编辑掉几乎所有的输出:
>>> d()
Small block threshold = 512, in 64 size classes.
class size num pools blocks in use avail blocks
----- ---- --------- ------------- ------------
...
8 72 17941 1004692 4
Run Code Online (Sandbox Code Playgroud)
由于我们创建了 100 万个元组,因此使用 1004692 个块的大小类是我们想要的一个很好的赌注 ;-) 每个块消耗 72 个字节。
切换到frozensets,输出显示每个消耗224个字节,多3倍多一点:
>>> tups = [frozenset(t) for t in tups]
>>> d()
Small block threshold = 512, in 64 size classes.
class size num pools blocks in use avail blocks
----- ---- --------- ------------- ------------
...
27 224 55561 1000092 6
Run Code Online (Sandbox Code Playgroud)
在这种特殊情况下,您得到的另一个答案恰好给出了相同的结果:
>>> import sys
>>> sys.getsizeof(tuple("abc"))
72
>>> sys.getsizeof(frozenset(tuple("abc")))
224
Run Code Online (Sandbox Code Playgroud)
虽然这通常是正确的,但并非总是如此,因为对象可能需要分配比实际需要更多的字节,以满足硬件对齐要求。 getsizeof()对此一无所知,但_debugmallocstats()显示了 Python 的小对象分配器实际需要使用的字节数。
例如,
>>> sys.getsizeof("a")
50
Run Code Online (Sandbox Code Playgroud)
在 32 位机器上,实际上需要使用 52 个字节,以提供 4 字节对齐。在 64 位机器上,目前需要 8 字节对齐,因此需要使用 56 字节。在 Python 3.8(尚未发布)下,在 64 位盒子上需要 16 字节对齐,并且需要使用 64 字节。
但是忽略所有这些,元组总是比具有相同元素数量的任何形式的集合需要更少的内存 - 甚至比具有相同元素数量的列表更少。