python - 为什么protobuf的内存比python中的普通dict+list小?
问题描述
我在嵌套的字典/列表中有大量的原始类型结构。结构相当复杂,并不重要。
如果我用 python 的内置类型 ( dict
/ list
/ float
/ int
/ str
) 表示它,它需要 1.1 GB,但如果我将它存储protobuf
并加载到内存中,它会小得多。总共约 250 MB。
我想知道这怎么可能。与某些外部库相比,python 中的内置类型效率低吗?
编辑:结构是从 json 文件加载的。所以对象之间没有内部引用
解决方案
“简单”python 对象,例如int
or float
,比它们使用的 C 对象需要更多的内存protobuf
。
让我们以一个list
Python 整数为例与整数数组进行比较,例如在array.array
(ie array.array('i', ...)
) 中。
分析array.array
很简单:丢弃 -object 的一些开销,array.arrays
每个元素只需要 4 个字节(C 整数的大小)。
对于整数列表,情况完全不同:
- 该列表不包含整数对象本身,而是指向对象的指针(
8
64位可执行文件的附加字节) - 即使是一个小的非零整数也至少需要
28
字节(参见import sys; sys.getsizeof(1)
返回 28):引用计数需要 8 个字节,保存指向整数函数表的指针需要 8 个字节,整数值的大小需要 8 个字节( Python 的整数可以远大于 2^32),并且至少有 4 个字节来保存整数值本身。 - 还有4.5 字节的内存管理开销。
long long int
这意味着与可能的 4 个字节(如果我们使用,即 64 位整数,则为 8 个字节)相比,每个 Python 整数的成本高达 40.5 个字节。
doubles
与( ie )的数组相比,具有 Python 浮点数的列表的情况类似,array.array('d',...)
每个元素只需要大约 8 个字节。但是对于列表,我们有:
- 该列表不包含浮点对象本身,而是指向对象的指针(
8
64 位可执行文件的附加字节) - 浮点对象需要
24
字节(参见import sys; sys.getsizeof(1.0)
返回 24):引用计数需要 8 个字节,保存指向浮点函数表的指针需要 8 个字节,保存double
-value 本身需要 8 个字节。 - 因为 24 是 8 的倍数,所以内存管理的开销“仅”约为 0.5 个字节。
这意味着 Python 浮点对象为 32.5 字节,而 C-double 为 8 字节。
protobuf
在内部使用与数据相同的表示,array.array
因此需要更少的内存(如您所见,大约少 4-5 倍)。numpy.array
是数据类型的另一个示例,它保存原始 C 值,因此比列表需要更少的内存。
如果不需要在字典中搜索,那么将键值对保存在列表中将比在字典中需要更少的内存,因为不必维护用于搜索的结构(这会增加一些内存成本) ) - 这也是导致protobuf
-data 内存占用更小的另一件事。
回答你的另一个问题:没有内置模块适用于 Python- dict
,什么array.array
适用于 Python- list
,所以我借此机会厚颜无耻地为我的库插入广告:cykhash
.
集合和映射cykhash
需要不到 25%的 Python内存,但速度dict
差不多。set
推荐阅读
- python - 如何在 Pandas 中使用 Groupby() 回填和转发对应于每个月的开始和结束的填充值
- java - 如何在工具栏Android中居中按钮
- prolog - Prolog:例如,X 是 3 和 3 是 X 有什么区别?
- database - 使用 WhiteDB 的图形数据库
- ansible - Ansible,如何链接 regex_replace 语句?
- python - Python:从数据集中创建 5-6 个组,以便这些组在 3 个不同的变量(十分位数、人口规模和区域)之间保持平衡
- python - 如何使用字典循环遍历不同的文件名以在python中创建多个数据库?
- javascript - Chart.js V3.20 过滤图例标签不起作用
- sql - 在字符串中的第三次和第四次出现之间搜索
- r - 如何用文本替换表情符号并将它们视为单个单词?