哪个需要较少的内存,frozenset或tuple?

时间:2019-07-07 04:44:14

标签: python memory-management tuples frozenset

我有一个对象,需要用0-3个字符串(在20种可能性中)进行“标记”;这些值都是唯一的,顺序无关紧要。唯一需要对标签执行的操作就是检查是否存在特定的标签(specific_value in self.tags)。

但是,一次在内存中存在大量的这些对象,以至于它突破了我的旧计算机RAM的极限。因此节省几个字节就可以加起来。

每个对象上的标签太少了,我怀疑查找时间会变得很重要。但是:在这里使用元组和Frozenset之间有内存差异吗?还有其他真正的理由要使用另一个吗?

4 个答案:

答案 0 :(得分:4)

sys.getsizeof似乎是您想要的stdlib选项...但是我对您的整个用例感到不满意

import sys
t = ("foo", "bar", "baz")
f = frozenset(("foo","bar","baz"))
print(sys.getsizeof(t))
print(sys.getsizeof(f))

https://docs.python.org/3.7/library/sys.html#sys.getsizeof

  

所有内置对象都将返回正确的结果,但是对于第三方扩展,这不一定成立,因为它是特定于实现的。

...所以请不要对这种解决方案感到不适

编辑:显然@TimPeters的答案更正确...

答案 1 :(得分:3)

音色非常紧凑。集合基于哈希表,并且依赖于具有“空”插槽以减少哈希冲突的可能性。

对于最新版本的CPython,sys._debugmallocstats()显示许多可能有趣的信息。在64位Python 3.7.3下:

>>> from sys import _debugmallocstats as d
>>> tups = [tuple("abc") for i in range(1000000)]

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

自从我们创建了一百万个元组以来,我们非常希望使用1004692个块的size类是我们想要的;-)每个块占用72个字节。

切换到冻结集,输出显示,每个冻结集消耗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

在这种情况下,您得到的另一个答案碰巧给出了相同的结果:

>>> import sys
>>> sys.getsizeof(tuple("abc"))
72
>>> sys.getsizeof(frozenset(tuple("abc")))
224

虽然这通常是正确的,但并非总是如此,因为对象可能需要比实际需要的分配个字节更多的字节,才能满足硬件对齐要求。 getsizeof()对此一无所知,但是_debugmallocstats()显示了Python的小对象分配器实际需要使用的字节数。

例如,

>>> sys.getsizeof("a")
50

在32位的盒子上,实际上需要使用52个字节来提供4个字节的对齐方式。在64位设备上,当前需要8字节对齐,因此需要使用56个字节。在Python 3.8(尚未发布)下,在64位盒上需要16字节对齐,并且将需要使用64字节。

但是忽略所有这些,元组将总是需要比具有相同数量的元素的任何形式的set更少的内存,甚至比具有相同数量的元素的列表所需要的内存更少。

答案 2 :(得分:2)

如果您想节省内存,请考虑

  • 通过提取将标签存在的数据结构提取到外部(单个)数据结构中,以节省一些内存来表现出优雅的风格
  • 使用“标志”(位图)类型的方法,其中每个标签都映射到32位整数的一位。然后,您所需要做的就是从对象(身份)到32位整数(标志)的(单个)dict映射。如果没有标记,则词典中没有条目。

答案 3 :(得分:1)

`如果用recordclass库中的类型替换元组,有可能减少内存:

>>> from recordclass import make_arrayclass
>>> Triple = make_arrayclass("Triple", 3)
>>> from sys import getsizeof as sizeof
>>> sizeof(Triple("ab","cd","ef"))
40
>>> sizeof(("ab","cd","ef"))
64

差异等于sizeof(PyHC_Head) + sizeof(Py_ssize_t)

P.S .:在64位Python 3.8上测量数字。