【发布时间】:2018-06-11 06:57:22
【问题描述】:
当字典增加到几千个键时,我在使用字典时遇到了严重的速度下降。
我正在处理一个包含约 1,000,000 行数据的文件,我正在使用字典构建一个类似图形的数据结构
这是我的瓶颈函数
def create_edge(node_a, node_b, graph):
if node_a not in graph.keys():
graph[node_a] = {node_b: 1}
elif node_b in graph[node_a].keys():
graph[node_a][node_b] += 1
else:
graph[node_a][node_b] = 1
create_edge 将创建从 node_a 到 node_b 的边,或者将它们之间的现有边的权重加 1。
由于我的节点由字符串唯一 id 标识,因此我使用字典进行存储,假设搜索是否存在键,并且插入平均需要 O(1)。
如果我注释掉 create_edge,我每秒可以处理大约 20,000 条记录,create_edge 作为我管道的一部分,每秒大约可以处理 20 条记录。
前 100 条记录大约需要 500 毫秒来处理。
当字典大小增加到 10,000 左右时 - 处理 100 条记录大约需要 15,000 毫秒,每个记录进程平均调用 create_edge 大约 4 次 - 因此,当字典大小为 10,000 时,对 create_edge 的 400 次调用需要 15 秒。
首先,这些运行时有意义吗?对我来说似乎很重要,如果我错了,请纠正我。
其次,我们将不胜感激优化字典使用以获得更好的运行时间的建议。
我希望字典大小至少为 100,000 以完成对全部 1,000,000 条记录的处理。
编辑:结论
你在钱上是对的,在这里犯了两个菜鸟错误.. :)
keys() 调用显着增加了复杂性,将其从恒定时间变为每次边插入的多时间(平方),将 if node in graph.keys() 替换为 if node in graph 会在约 300 毫秒内产生 100 条记录的恒定处理时间。
第二个错误是 virtualenv 配置,这让我相信我在使用 python3 而我实际上是在使用 python2。
python3 确实将 keys() 代码优化为恒定时间搜索,这对运行时间有好处,但对于正确的代码风格则不太好。
非常感谢您的帮助。
我在删除 keys() 调用后执行了运行时间比较。
# graph = {}
python version: 3.6.3
start time 11:44:56
Number of records: 1029493
graph created, 1231630 nodes
end time 11:50:35
total ~05:39
# graph = defaultdict(lambda : defaultdict(int))
python version: 3.6.3
start time 11:54:52
Number of records: 1029493
graph created, 1231630 nodes
end time 12:00:34
total ~05:42
# graph = {}
python version: 2.7.10
start time 12:03:25
Number of records: 1029493
graph created, 1231630 nodes
end time 12:09:40
total ~06:15
【问题讨论】:
-
这是什么 Python 版本?
-
你是否在使用 Python 2?
-
因为
.keys()得到一个键的List,而不是字典。因此,在create_edge内部,您正在执行两次O(n)操作以从转换为列表的整个字典中搜索两次。你会想做if node_a in graph,见this question。 注意 如下面的 cmets 所述,这仅适用于使用 Python 2 而非 3 时。 -
@SpencerWieczorek 这就是我的怀疑,因为它是一个著名的 Python 反模式,但在 Python 3 中,它返回具有 O(1) 成员资格测试的键的 view .
-
@AlanSTACK 不,它确实不返回一个迭代器,尽管
dict_keys对象是可迭代的。不相信我?试试next({}.keys())
标签: python