IPython里针对line_profiler和memory_profiler的快捷方式

Line_profiler和memory_profiler共有的特性是它们都在IPython里有快捷方式。你只需要在IPython里输入以下内容:

1
2
% load_ext memory_profiler
% load_ext line_profiler

完成这个步骤后,你就可以使用一个神奇的命令 %lprun 和 %mprun ,它们跟其对应的命令行的功能是类似的。主要的不同是在这里你不需要在你想测量的函数上面使用@profiler来装饰它。可以直接在IPython里像一下的样子了来运行它:

1
2
3
4
% load_ext In [ 1 ]: from primes import primes
In [ 2 ]: % mprun - f primes primes( 1000 )
In [ 3 ]: % lprun - f primes primes( 1000 / pre>
这个因为其不用修改你的代码,而能够节省你很多的时间和精力。

哪里有内存泄漏?

C Python解释器使用引用计数的方法来作为其内存管理的主要方法。这意味着虽有对象都包含一个计数器,如果增加了一个对这个对象的引用就加1,如果引用被删除就减1。当计数器的值变成0的时候,C Python解释器就知道这个对象不再被使用便会删除这个对象并且释放它占用的内存。 如果在你的程序里,尽管一个对象不再被使用了,但仍然保持对这个对象的引用,就会导致内存泄漏。 找到这些内存泄漏最快的方法就是使用一个很棒的工具,名叫objgraph,由Marius Gedminas写的。这个工具能让你看到内存里的对象数量,也能在你的代码里定位保持对这些对象的引用的地方。 首先是安装objgraph:

1
pip install objgraph

在它安装好之后,在你的代码里添加一段声明来调用debugger。

1
import pdb; pdb.set_trace()

哪些对象是最常见的?

在运行时,你可以通过运行它考察在你的代码里排前20最常见的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(pdb) import objgraph
(pdb) objgraph.show_most_common_types()
MyBigFatObject 20000
tuple 16938
function 4310
dict 2790
wrapper_descriptor 1181
builtin_function_or_method 934
weakref 764
list 634
method_descriptor 507
getset_descriptor 451
type 439

哪些对象被添加或者删除?

我们也可以及时看到在两点之间那些对象被添加或者删除了:

1
2
3
4
5
6
7
8
9
10
11
12
(pdb) import objgraph
(pdb) objgraph.show_growth()
.
.
.
(pdb) objgraph.show_growth() # this only shows objects that has been added or deleted since last show_growth() call
traceback 4 + 2
KeyboardInterrupt 1 + 1
frame 24 + 1
list 667 + 1
tuple 16969 + 1

哪里引用了有漏洞的对象

顺着这条路继续,我们也能看到哪里有对任何指定对象的引用是被保持了的。我们以下面的程序为例:

1
2
3
x = [ 1 ]
y = [x, [x], { "a" :x}]
import pdb; pdb.set_trace()

为了看哪里有对于变量x的一个引用,运行objgraph.show_backref( )函数:

1
2
(pdb) import objgraph
(pdb) objgraph.show_backref([x], filename = "/tmp/backrefs.png" )

这个命令的输出应该是一个PNG图片,它的路径为 /tmp/backrefs.png。它看起来应该是这样:

backrefs

最下面的方框,里面用红色字母写出的是我们感兴趣的对象。我们可以看到它被变量x引用一次,被列表y引用三次。如果x是导致内存泄漏的对象,我们可以用这个方法来看为什么它没有通过追踪所有的引用而被自动释放。

来回顾一下,objgraph 能让我们:

  • 显示我们的python程序里占用内存最多的前N个对象

  • 显示在一段时间里被添加或删除的对象

  • 显示在我们的代码里对一个给定对象的所有引用

成就vs 精确

在前文中,我已经展示了如果使用几种工具来分析Python程序的性能。在有了这些工具和技术后,你应该能得到所需要的所有信息来追踪Python程序里大部分的内存泄漏和性能瓶颈。

跟很多其他的主题一样,进行一个性能分析意味着平衡和取舍。在不确定的时候,实现最简单的方案将是适合你目前需要的。

本文由 伯乐在线 - 贱圣OMG 翻译自 Huy Nguyen。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。


【感谢 贱圣OMG 的热心翻译。如果其他朋友也有不错的原创或译文,可以提交到伯乐在线。】