`namedtuple` 在内存使用方面真的和元组一样高效吗?我的测试说不
Is `namedtuple` really as efficient in memory usage as tuples? My test says NO
Python 文档中指出,namedtuple
的优点之一是它与元组一样 内存高效 。
为了验证这一点,我使用了 iPython 和 ipython_memory_usage。测试如下图所示:
测试表明:
10000000
个 namedtuple
的实例使用了大约 850 MiB
的 RAM
10000000
tuple
个实例使用了大约 73 MiB
RAM
10000000
dict
个实例使用了大约 570 MiB
RAM
所以 namedtuple
使用 比 tuple
多 内存!甚至 比 dict
!!
你怎么看?我哪里错了?
一个更简单的指标是检查等效 tuple
和 namedtuple
对象的大小。给定两个大致相似的对象:
from collections import namedtuple
import sys
point = namedtuple('point', 'x y z')
point1 = point(1, 2, 3)
point2 = (1, 2, 3)
获取它们在内存中的大小:
>>> sys.getsizeof(point1)
72
>>> sys.getsizeof(point2)
72
它们在我看来是一样的...
更进一步复制您的结果,请注意,如果您按照自己的方式创建相同元组的列表,每个 tuple
都是完全相同的对象:
>>> test_list = [(1,2,3) for _ in range(10000000)]
>>> test_list[0] is test_list[-1]
True
因此在您的元组列表中,每个索引都包含一个 相同 对象的引用。没有10000000个元组,1个元组有10000000个引用
另一方面,您的 namedtuple
个对象列表实际上 确实 创建了 10000000 个唯一对象。
更好的同类比较是查看
的内存使用情况
>>> test_list = [(i, i+1, i+2) for i in range(10000000)]
和:
>>> test_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
它们的大小相同:
>>> sys.getsizeof(test_list)
81528056
>>> sys.getsizeof(test_list_n)
81528056
自己做一些调查(使用 Python 3.6.6)。我运行得出以下结论:
在所有三种情况下(元组列表、命名元组列表、字典列表)。 sys.getsizeof returns 列表的大小,它只存储引用。所以在所有三种情况下你都得到 size: 81528056 。
基本类型的大小为:
sys.getsizeof((1,2,3))
72
sys.getsizeof(point(x=1, y=2, z=3))
72
sys.getsizeof(dict(x=1, y=2, z=3))
240
命名元组的时机非常糟糕:
元组列表:1.8s
命名元组列表:10s
字典列表:4.6s
查看系统负载,我开始怀疑 getsizeof 的结果。
在测量 Ptyhon3 进程的占用空间后,我得到:
test_list = [(i, i+1, i+2) for i in range(10000000)]
增加:1 745 564K
每个元素大约 175B
test_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
增加:1 830 740K
每个元素大约 183B
test_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
增加:2 717 492 K
每个元素大约 272B
Python 文档中指出,namedtuple
的优点之一是它与元组一样 内存高效 。
为了验证这一点,我使用了 iPython 和 ipython_memory_usage。测试如下图所示:
测试表明:
10000000
个namedtuple
的实例使用了大约850 MiB
的 RAM10000000
tuple
个实例使用了大约73 MiB
RAM10000000
dict
个实例使用了大约570 MiB
RAM
所以 namedtuple
使用 比 tuple
多 内存!甚至 比 dict
!!
你怎么看?我哪里错了?
一个更简单的指标是检查等效 tuple
和 namedtuple
对象的大小。给定两个大致相似的对象:
from collections import namedtuple
import sys
point = namedtuple('point', 'x y z')
point1 = point(1, 2, 3)
point2 = (1, 2, 3)
获取它们在内存中的大小:
>>> sys.getsizeof(point1)
72
>>> sys.getsizeof(point2)
72
它们在我看来是一样的...
更进一步复制您的结果,请注意,如果您按照自己的方式创建相同元组的列表,每个 tuple
都是完全相同的对象:
>>> test_list = [(1,2,3) for _ in range(10000000)]
>>> test_list[0] is test_list[-1]
True
因此在您的元组列表中,每个索引都包含一个 相同 对象的引用。没有10000000个元组,1个元组有10000000个引用
另一方面,您的 namedtuple
个对象列表实际上 确实 创建了 10000000 个唯一对象。
更好的同类比较是查看
的内存使用情况>>> test_list = [(i, i+1, i+2) for i in range(10000000)]
和:
>>> test_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
它们的大小相同:
>>> sys.getsizeof(test_list)
81528056
>>> sys.getsizeof(test_list_n)
81528056
自己做一些调查(使用 Python 3.6.6)。我运行得出以下结论:
在所有三种情况下(元组列表、命名元组列表、字典列表)。 sys.getsizeof returns 列表的大小,它只存储引用。所以在所有三种情况下你都得到 size: 81528056 。
基本类型的大小为:
sys.getsizeof((1,2,3)) 72
sys.getsizeof(point(x=1, y=2, z=3)) 72
sys.getsizeof(dict(x=1, y=2, z=3)) 240
命名元组的时机非常糟糕:
元组列表:1.8s
命名元组列表:10s
字典列表:4.6s查看系统负载,我开始怀疑 getsizeof 的结果。 在测量 Ptyhon3 进程的占用空间后,我得到:
test_list = [(i, i+1, i+2) for i in range(10000000)]
增加:1 745 564K
每个元素大约 175Btest_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
增加:1 830 740K
每个元素大约 183Btest_list_n = [point(x=i, y=i+1, z=i+2) for i in range(10000000)]
增加:2 717 492 K
每个元素大约 272B