【问题标题】:How to optimize the memory and time usage of the following algorithm in python如何在python中优化以下算法的内存和时间使用
【发布时间】:2012-06-28 14:40:30
【问题描述】:

我正在尝试在 Python 中完成以下逻辑操作,但遇到了内存和时间问题。因为,我对 python 很陌生,所以关于如何以及在哪里优化问题的指导将不胜感激! (我明白下面的问题有些抽象)

import networkx as nx 
    dic_score = {}
    G = nx.watts_strogatz_graph(10000,10,.01) # Generate 2 graphs with 10,000 nodes using Networkx
    H = nx.watts_strogatz_graph(10000,10,.01)
    for Gnodes in G.nodes()
        for Hnodes in H.nodes ()  # i.e. For all the pair of nodes in both the graphs
           score = SomeOperation on (Gnodes,Hnodes)  # Calculate a metric 
           dic_score.setdefault(Gnodes,[]).append([Hnodes, score, -1 ]) # Store the metric in the form a Key: value, where value become a list of lists, pair in a dictionary

然后根据这里提到的标准对生成的字典中的列表进行排序 sorting_criterion

我的问题/疑问是:

1) 有没有比使用 for 循环进行迭代更好的方法来解决这个问题?

2) 解决上述问题的最优化(最快)方法应该是什么?我应该考虑使用字典以外的其他数据结构吗?或者可能是文件操作?

3) 由于我需要对这个字典中的列表进行排序,它有 10,000 个键,每个键对应于 10,000 个值的列表,因此内存需求很快变得巨大,我用完了。

3) 有没有办法将排序过程集成到字典本身的计算中,即避免单独循环排序?

任何意见将不胜感激!谢谢!

【问题讨论】:

  • 10^4 x 10^4 节点 = 10^8。当然它很慢。如果没有您正在尝试执行的操作的信息,则运行时间和内存使用情况很难改善。
  • @nhahtdh:基本上,循环代表提取与每个节点关联的向量。我有兴趣从两个图中说出这些向量的所有可能对的点积,即 10000*10000 对。因此,每个节点都将与 10000 个这样的点积相关联,然后我想对其进行排序并保存以供进一步处理。正如您正确提到的,计算时间很长,内存需求也很大,我想优化,可能是通过一些文件操作或您可能有的任何其他建议。

标签: python performance sorting memory networkx


【解决方案1】:

1) 您可以使用 itertools 模块中的功能之一。顺便提一下,你可以看说明书或者打电话:

from itertools import product
help(product)

这是一个例子:

for item1, item2 in product(list1, list2):
    pass

2) 如果结果太大而无法放入内存,请尝试将它们保存在某个地方。您可以将其输出为 CSV 文件,例如:

with open('result.csv') as outfile:
   writer = csv.writer(outfile, dialect='excel')
   for ...
       writer.write(...)

这会释放你的记忆。

3) 我认为最好在事后对结果数据进行排序(因为sort 函数相当快)而不是使事情复杂化并即时对数据进行排序。

您可以改为使用NumPy arroy/matrix 运算(求和、乘积,甚至将函数映射到每个矩阵行)。这些速度如此之快,以至于有时过滤数据的成本比计算所有内容的成本还要高。

如果您的应用仍然很慢,请尝试对其进行分析,以准确了解哪些操作很慢或执行次数过多:

from cProfile import Profile
p = Profile()

p.runctx('my_function(args)', {'my_function': my_function, 'args': my_data}, {})
p.print_stats()

你会看到表格:

      2706 function calls (2004 primitive calls) in 4.504 CPU seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     2    0.006    0.003    0.953    0.477 pobject.py:75(save_objects)
  43/3    0.533    0.012    0.749    0.250 pobject.py:99(evaluate)
...

【讨论】:

  • 非常感谢您的详细回答。让我处理您的反馈并观察结果。
  • NumPy 数组也具有内存效率。
【解决方案2】:

其他人提到了itertools.product。这很好,但在您的情况下,还有另一种可能性:内部循环的生成器表达式和 sorted 函数。 (当然,代码未经测试。)

import networkx as nx
from operator import itemgetter 
dic_score = {}
G = nx.watts_strogatz_graph(10000,10,.01) # Generate 2 graphs with 10,000 nodes using Networkx
H = nx.watts_strogatz_graph(10000,10,.01)
for Gnodes in G.nodes():
    dic_score[Gnodes] = sorted([Hnodes, score(Gnodes, Hnodes), -1] for Hnodes in H.nodes(), key=operator.itemgetter(1)) # sort on score

内部循环被生成器表达式替换。它也是动态排序的(假设您想对score 上的每个内部列表进行排序)。您可以轻松地将每个内部列表写入文件,而不是存储在字典中,这将有助于内存。

【讨论】:

  • 感谢您的回答。我在这里不确定,因为我正在学习 Python,但遇到了一个 cPickle 模块。无论如何在每个内部循环之后使用它都会帮助我的事业吗?
  • 如果内存是您的主要问题,您可以将dic_score[Gnodes] = ... 替换为将结果写入磁盘。我对 (c)Pickle 的了解不够,无法帮助您解决此问题,但我认为它会起作用。
【解决方案3】:

使用返回列表的函数时,请检查返回迭代器的函数。

这将提高内存使用率。

在您的情况下,nx.nodes 返回完整列表。见:nodes

使用nodes_iter,因为它返回一个迭代器。这应该确保您在 for 循环中迭代节点时没有内存中的完整节点列表。

见:nodes_iter

一些改进:

import networkx as nx 
    dic_score = {}
    G = nx.watts_strogatz_graph(10000,10,.01) 
    H = nx.watts_strogatz_graph(10000,10,.01)
    for Gnodes in G.nodes_iter() ----------------> changed from G.nodes()
        for Hnodes in H.nodes_iter()  -----------> changed from H.nodes()
           score = SomeOperation on (Gnodes,Hnodes) 
           dic_score.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])

您也可以使用另一个习惯用法,因为现在您有两个迭代器:使用 itertools.products

product(A, B) returns the same as ((x,y) for x in A for y in B). 

【讨论】:

  • 这真的很有帮助!感谢您的见解。我正在尝试实施您的所有建议。
猜你喜欢
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 2017-07-19
  • 2011-02-07
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多