为什么protobuf在内存中比python中的普通dict + list小?

use*_*014 7 python json protocol-buffers python-3.x

我在嵌套的 dict/list 中有一个大型的原始类型结构。结构相当复杂,并不重要。

如果我用 python 的内置类型 ( dict/ list/ float/ int/ str) 表示它需要 1.1 GB,但如果我将它存储protobuf并加载到内存中,它会小得多。总共约 250 MB。

我想知道这怎么可能。与某些外部库相比,python 中的内置类型是否效率低下?

编辑:结构是从 json 文件加载的。所以对象之间没有内部引用

ead*_*ead 9

“简单”python 对象,例如intfloat,需要比protobuf.

让我们以 a listof Python 整数作为示例与整数数组进行比较,例如在array.array(ie array.array('i', ...)) 中。

for 的分析array.array很简单:从array.arrays-object 中丢弃一些开销,每个元素只需要 4 个字节(C 整数的大小)。

对于整数列表,情况完全不同:

  • 该列表不包含整数对象本身,而是指向对象的指针(864 位可执行文件的附加字节)
  • 即使是一个小的非零整数也至少需要28字节(参见import sys; sys.getsizeof(1)返回 28):引用计数需要 8 个字节,保存指向整数函数表的指针需要 8 个字节,整数值的大小需要 8 个字节( Python 的整数可以比 2^32 大得多),并且至少需要 4 个字节来保存整数值本身。
  • 还有4.5 字节的内存管理开销

这意味着与可能的 4 个字节(如果我们使用 8 个字节long long int,即 64 位整数)相比,每个 Python 整数的成本高达 40.5 个字节。

doubles( ie array.array('d',...))的数组相比,带有 Python 浮点数的列表的情况类似,每个元素只需要大约 8 个字节。但是对于列表,我们有:

  • 该列表不包含浮点对象本身,而是指向对象的指针(864 位可执行文件的附加字节)
  • 浮点对象需要24字节(参见import sys; sys.getsizeof(1.0)返回 24):引用计数需要 8 个字节,8 个字节用于保存指向浮点函数表的指针,8 个字节用于保存double-value 本身。
  • 因为 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.

Sets 和 maps fromcykhash 需要不到Python'S- dict/set内存的25%,但速度差不多。