Python OrderDict与dict()的比较

Python OrderDict与dict()的比较,python,loops,ordereddictionary,Python,Loops,Ordereddictionary,这件事让我完全困惑不解 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_i

这件事让我完全困惑不解

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行并从collections行删除注释时,事情变得非常奇怪。其中大约有400万行正在生成并附加到asset_hist

所以,当我使用row=dict时,整个循环在大约10毫秒内完成,速度非常快。当我使用订购的词典时,我已经等了10多分钟,但仍未完成。现在,我知道OrderDict应该比dict慢一点,但最坏情况下应该慢10倍左右,根据我的数学计算,这个函数实际上慢10万倍左右

我决定在最低的循环中打印索引,看看发生了什么。有趣的是,我注意到控制台输出中有一个溅射。索引将在屏幕上以非常快的速度打印,然后在继续之前停止大约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(长时间等待…等待20分钟后放弃!内存使用率为88.3%,进程仍在运行)

等待发生的位置随每次运行而变化

编辑:再次运行,这次它在387333停止,靠近之前停止的位置。它在形成行后停止,同时尝试附加。。。我没有注意到最后一次尝试,但它当时也在那里。。。问题在于追加行,而不是行。。。我仍然感到困惑。这是它在长停止之前生成的行(将该行添加到打印语句中)。。。更改主机名以保护无辜者:


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

正如您自己的测试所证明的,您的内存正在耗尽。即使在CPython 3.6上(普通的
dict
实际上是被订购的,尽管还不能作为语言保证),
OrderedDict
dict
相比,内存开销也很大;它仍然使用一个边带链表来实现,以保持顺序并支持简单的迭代、使用
移动到\u end
等进行重新排序。您只需通过检查
sys.getsizeof
(确切的结果将因Python版本和构建位宽度而异,32位与64位):

忽略存储的数据,
OrderedDict
的开销几乎是普通
dict
的两倍。如果你在我的机器上制造了400万个这样的项目,那么会增加超过850MB的开销(3.5和3.6版本)

很可能是系统上所有其他程序的组合,加上Python程序,超过了分配给机器的RAM,而您陷入了交换振荡。特别是,每当
asset\u hist
必须为新条目展开时,它可能需要对大部分内容进行分页(由于缺少使用而被分页),并且每当触发循环垃圾回收运行时(默认情况下,大约每70000次分配和释放就会发生一次完整的GC),所有的
OrderedDict
s都会被调回,以检查它们是否仍在循环之外被引用(您可以通过禁用循环GC来检查GC运行是否是主要问题)

考虑到您的特定用例,我强烈建议您避免使用
dict
orderedict
。即使是Python3.6上更便宜的格式,当您反复使用一组正好有三个固定键时,dict的开销也有点极端。相反,它是为可通过名称或索引引用的轻量级对象而设计的(它们的行为类似于常规的
元组
s,但也允许将每个值作为命名属性进行访问),这将显著降低程序的内存成本(甚至在内存不成问题的情况下也可能加快程序的速度)

例如:

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))
使用中唯一的区别是将
行['computer\u name']
替换为
行.computer\u name
,或者如果需要所有值,可以像常规的
元组一样解压,例如
comphost、idx、hist=row
。如果您临时需要一个真正的
OrderedDict
(不要全部存储它们),您可以调用
行。_asdict()
以获得一个
OrderedDict
,其映射与
namedtuple
相同,但通常不需要。节省内存是有意义的;在我的系统中,三元素
namedtuple
将每项开销降低到72字节,甚至不到3.6
dict
的三分之一,也不到3.6
OrderedDict
的六分之一(三元素
namedtuple
在3.5上仍然是72字节,其中
dict
/
OrderedDict
在3.6之前更大)。它可以节省更多的钱
tuple
s(扩展名为
namedtuple
)被分配为单个连续的C
struct
,而
dict
和company至少是两个分配(一个用于对象结构,一个或多个用于结构的可动态调整大小的部分),其中每一项都可能支付分配器管理费用和调整成本

无论哪种方式,对于400万行的场景,使用
namedtuple
将意味着支付(超出价值成本)总计约275MB的开销,而不是。
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))