【问题标题】:Python OrderDict sputtering as compared to dict()与 dict() 相比,Python OrderedDict 溅射
【发布时间】:2017-12-27 15:36:39
【问题描述】:

这个让我完全困惑。

asset_hist = []
for key_host, val_hist_list in am_output.asset_history.items():
    for index, hist_item in enumerate(val_hist_list):
        #row = collections.OrderedDict([("computer_name", key_host), ("id", index), ("hist_item", hist_item)])
        row = {"computer_name": key_host, "id": index, "hist_item": hist_item}
        asset_hist.append(row)

此代码与注释掉的集合行完美配合。但是,当我注释掉 row = dict 行并从集合行中删除注释时,事情变得非常奇怪。大约有 400 万行正在生成并附加到asset_hist。

因此,当我使用 row=dict 时,整个循环在大约 10 毫秒内完成,快如闪电。当我使用有序字典时,我已经等了 10 多分钟,它仍然没有完成。现在,我知道 OrderDict 应该比 dict 慢一点,但在最坏的情况下它应该慢 10 倍左右,根据我的数学计算,这个函数实际上慢了大约 100,000 倍。

我决定在最低循环中打印索引以查看发生了什么。有趣的是,我注意到控制台输出中的溅射。索引会在屏幕上快速打印,然后停止大约 3-5 秒,然后再继续。

am_output.asset_history 是一个字典,它有一个键,主机,每一行都是一个字符串列表。例如

am_output.asset_history = {"host1": ["string1", "string2", ...], "host2": ["string1", "string2", ...], ...}

编辑:使用 OrderedDict 进行溅射分析

此 VM 服务器上的总内存:仅 8GB...需要获得更多配置。

循环编号

184796(约 5 秒等待,约 60% 内存使用)

634481(约 5 秒等待,约 65% 内存使用)

1197564(约 5 秒等待,约 70% 内存使用)

1899247(约 5 秒等待,约 75% 内存使用)

2777296(约 5 秒等待,约 80% 内存使用)

3873730(LONG WAIT...等了20分钟放弃了!,内存使用率88.3%,进程还在运行)

每次运行时等待发生的位置都会发生变化。

编辑:再次运行它,这次它停在 3873333,靠近它之前停止的位置。它在形成行后停止,同时尝试追加......我没有注意到最后一次尝试,但它也在那里......问题在于追加线,而不是行线......我还在莫名其妙。这是它在长时间停止之前生成的行(将该行添加到打印语句)...更改了主机名以保护无辜者:

3873333: OrderedDict([('computer_name', 'bg-fd5612ea'), ('id', 1), ('hist_item', "sys1 Normalizer (sys1-4): 域名无法从 sys1 Name 确定'bg-fd5612ea'。")])

【问题讨论】:

  • 我很难相信您所观察到的是由于dict 与OrderedDict 的行为,是否有可能发生其他变化?我也认为有人会努力重现这一点
  • 是的,他们需要首先生成巨大的字典,可能随机字符串生成器就足够了。但我是认真的,当我将 # 向下移动一行时,就会发生这种情况。还值得注意的是:当它在使用有序字典时卡在这个循环中并且我按 control+C 停止循环时,就像什么都没有发生......我必须按 control+Z 才能退出。
  • 哪个 Python 版本?在最新版本中,OrderedDict 只是 dict 的别名,因为当前的实现恰好按实现细节排序。
  • python 3. 我在这个软件中经常使用 OrderedDicts,它们很棒......这是关于这个特定循环的东西。
  • OrderedDicts 有很多,与普通的dicts 相比并没有那么紧凑。您能否在此过程中检查您的内存使用情况?

标签: python loops ordereddictionary


【解决方案1】:

正如您自己的测试所证明的,您的内存不足。即使在 CPython 3.6 上(实际上订购了普通的 dict,尽管还没有作为语言保证),与 dict 相比,OrderedDict 具有显着的内存开销;它仍然使用边带链表实现,以保持顺序并支持轻松迭代,使用move_to_end 重新排序等。您可以通过检查sys.getsizeof 来判断(确切结果将因 Python 版本和构建位宽而异,32与 64 位相比):

>>> od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
>>> d = {**od}
>>> sys.getsizeof(od)
464   # On 3.5 x64 it's 512
>>> sys.getsizeof(d)
240   # On 3.5 x64 it's 288

忽略存储的数据,这里OrderedDict 的开销几乎是普通dict 的两倍。如果您要生产 400 万件此类物品,那么在我的机器上会增加超过 850 MB 的开销(在 3.5 和 3.6 上)。

很可能您系统上所有其他程序以及您的 Python 程序的组合超过了分配给您机器的 RAM,并且您陷入了交换抖动的困境。特别是,每当asset_hist 必须为新条目扩展时,它可能需要对其中的大部分进行分页(由于缺乏使用而被分页),并且每当触发循环垃圾收集运行时(大约每隔默认情况下有 70,000 次分配和释放),所有OrderedDicts 都会被调回以检查它们是否仍然在循环之外被引用(您可以通过禁用循环 GC via gc.disable() 来检查 GC 运行是否是主要问题)。

鉴于您的特定用例,我强烈建议您同时避免使用 dict 和 OrderedDict。即使是dict 的开销,即使是 Python 3.6 上更便宜的形式,当你一遍又一遍地拥有一组恰好三个固定键时,它的开销就有点极端了。取而代之的是use collections.namedtuple,它专为可通过名称或索引引用的轻量级对象而设计(它们的作用类似于常规的tuples,但也允许将每个值作为命名属性访问),这将大大降低程序的内存成本(即使内存不是问题,也可能会加快速度)。

例如:

from collections import namedtuple

ComputerInfo = namedtuple('ComputerInfo', ['computer_name', 'id', 'hist_item'])

asset_hist = []
for key_host, val_hist_list in am_output.asset_history.items():
    for index, hist_item in enumerate(val_hist_list):
        asset_hist.append(ComputerInfo(key_host, index, hist_item))

唯一的区别是你将row['computer_name']替换为row.computer_name,或者如果你需要所有的值,你可以像普通的tuple一样解压,例如comphost, idx, hist = row。如果您暂时需要一个真正的OrderedDict(不要为所有内容存储它们),您可以调用row._asdict() 以获取与namedtuple 具有相同映射的OrderedDict,但通常不需要这样做。内存节省是有意义的;在我的系统上,三个元素 namedtuple 将每个项目的开销降低到 72 个字节,甚至不到 3.6 dict 的三分之一,不到 3.6 OrderedDict 的六分之一(以及三个元素 namedtuple在 3.5 上保持 72 字节,其中 dict/OrderedDict 在 3.6 之前更大)。它可能会节省更多; tuples(和 namedtuple 通过扩展)被分配为单个连续的 C struct,而 dict 和 company 至少有两个分配(一个用于对象结构,一个或多个用于结构),每个都可能支付分配器开销和对齐成本。

无论哪种方式,对于您的 400 万行方案,使用 namedtuple 将意味着支付(超出值的成本)总计约 275 MB 的开销,而 @987654359 的开销为 915 (3.6) - 1100 (3.5) MB @ 和 1770 (3.6) - 1950 (3.5) MB 对于 OrderedDict。当您谈论 8 GB 系统时,将开销减少 1.5 GB 是一项重大改进。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-15
    • 2021-07-19
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 2018-03-26
    • 2019-01-19
    相关资源
    最近更新 更多