识别python中的内存泄漏 - Memory Profiler

时间:2016-02-19 17:28:48

标签: python memory memory-management memory-leaks

我是Python的新手,我目前正致力于解决问题,以提高我的编码技能。目前,我正处理一个问题,我必须stable sort输入并输出反向稳定的排序值。我已经对它进行了编程并在网站的在线评判中执行了代码,并且对于一个测试用例(不知道测试用例),我得到Memory Limit Exceeded错误。因此,经过一些研究,我了解到代码中发生了memory leak,代码并不完全有效。所以我已经安装了python' memory_profiler来监控进程的内存消耗以及对代码内存消耗的逐行分析。

请在下面找到从memory_profiler获取的输入详细信息,代码,输出和内存分析分析。

输入:

8
1 2
16 3
11 2
20 3
3 5
26 4
7 1
22 4 

代码:

from collections import OrderedDict
@profile
def test_1():
    print "Enter the number: "
    n = raw_input()
    k = []
    v = []
    print "Enter ID and M: "
    for i in range(0,int(n)):
        a, b = raw_input().split(' ')
        k.append(a)
        v.append(b)
    d = OrderedDict(zip(k,v))
    sorted_items = sorted(d.items(), key=lambda (k,v):int(v), reverse=True)
    for i, j in sorted_items:
        print i, j

if __name__ == '__main__':
        test_1()

输出:

    Line #    Mem usage    Increment   Line Contents
================================================
     2   10.520 MiB    0.000 MiB   @profile
     3                             def test_1():
     4   10.531 MiB    0.012 MiB        print "Enter the number: "
     5   10.551 MiB    0.020 MiB        n = raw_input()
     6   10.551 MiB    0.000 MiB        k = []
     7   10.551 MiB    0.000 MiB        v = []
     8   10.551 MiB    0.000 MiB        print "Enter ID and M: "
     9   10.551 MiB    0.000 MiB        for i in range(0,int(n)):
    10   10.551 MiB    0.000 MiB                a, b = raw_input().split(' ')
    11   10.551 MiB    0.000 MiB                k.append(a)
    12   10.551 MiB    0.000 MiB                v.append(b)
    13
    14   10.551 MiB    0.000 MiB        d = OrderedDict(zip(k,v))
    15   10.555 MiB    0.004 MiB        sorted_items = sorted(d.items(), key=lambda (k,v):int(v), reverse=True)
    16   10.555 MiB    0.000 MiB        for i, j in sorted_items:
    17   10.555 MiB    0.000 MiB                print i, j

预期输出(我能够获得所需的输出):

3 5
26 4
22 4
16 3
20 3
1 2
11 2
7 1

此代码对于更高输入或更高数字是否效率不高?从分析中我可以看到只有较少的内存利用,但对于那个特定的测试用例,我可以看到内存利用率超过16MB。 有人能告诉我我在哪里做错了。我的方法是错误的还是流程错了?你能否告诉我为什么我无法按预期获得输出。提前致谢。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

我正在撰写评论但是时间太长了,所以我想我不妨将其升级为答案。

首先,profile装饰器仅使用相当多的内存。如你所见:

from memory_profiler import profile
@profile
def foo():
    pass

我得到了

Line #    Mem usage    Increment   Line Contents
================================================
    2     28.5 MiB      0.0 MiB   @profile
    3                             def foo():
    4     28.5 MiB      0.0 MiB       pass

你的号码可能会有所不同(我正在运行Python 3,在IDE中也不少),但它与你的功能基本相同。几乎所有的内存使用都来自@profile行(10.520 MiB),你的函数添加的内容(参见增量列)很简单(0.36 MiB)。

结果是你看起来不应该有任何问题(如果你发布的内容已经是你的整个代码了,我想它就是这样)。我不知道哪个测试用例可能会给你Memory Limit Exceeded。我们真的需要知道测试用例是什么来调查问题。

尽管如此,一项改进可以使您的代码更有效,特别是对于大量输入。您无需在代码中构建中间列表(kv)。直接写到字典:

d = OrderedDict()
for i in range(int(n)):  # Note you don't need range(0, x); just range(x)
    a, b = raw_input().split()  # No need for an argument to split, either
    d[a] = b

更好的是,您可以避免for循环并使用更高效的生成器表达式:

d = OrderedDict(raw_input().split() for _ in range(int(n)))

生成器表达式是(foo something_like_a_for_loop)形式的表达式(形式描述here);如果它是函数的唯一参数,则可以省略周围的括号。它就像一个列表在很多方面:你可以使用for进行迭代,你可以使用list从中获取一个列表,只要有一个迭代器就可以使用它。但是当列表很长时,它比等效列表占用的空间要少得多。 (但也存在差异:gen expr可以在上迭代,无法编入索引,也没有len等等。您可以阅读更多相关信息{{ 3}}。)

您可以进行其他一些小改进。全部纳入下面的重写

def test_1():
    n = int(raw_input('Enter the number: '))
    d = OrderedDict(raw_input().split() for _ in range(n))
    sorted_items = sorted(d.items(), key=lambda k_v: int(k_v[1]), reverse=True)
    for i, j in sorted_items:
        print i, j