Python性能分析指南

虽然你所写的每个Python程序并不总是需要严密的性能分析,但是当这样的问题出现时,如果能知道Python生态系统中的许多种工具,这样总是可以让人安心的。

分析一个程序的性能可以归结为回答4个基本的问题:

1.它运行的有多块?

2.那里是速度的瓶颈?

3.它使用了多少内存?

4.哪里发生了内存泄漏?

下面,我们将用一些很酷的工具,深入细节的回答这些问题。

使用time工具粗糙定时

首先,我们可以使用快速然而粗糙的工具:古老的unix工具time,来为我们的代码检测运行时间。

上面三个输入变量的意义在文章 stackoverflow article 中有详细介绍。简单的说:

real – 表示实际的程序运行时间

user – 表示程序在用户态的cpu总时间

sys – 表示在内核态的cpu总时间

通过sys和user时间的求和,你可以直观的得到系统上没有其他程序运行时你的程序运行所需要的CPU周期。

若sys和user时间之和远远少于real时间,那么你可以猜测你的程序的主要性能问题很可能与IO等待相关。

使用计时上下文管理器进行细粒度计时

我们的下一个技术涉及访问细粒度计时信息的直接代码指令。这是一小段代码,我发现使用专门的计时测量是非常重要的:

timer.py

为了使用它,你需要用Python的with关键字和Timer上下文管理器包装想要计时的代码块。它将会在你的代码块开始执行的时候启动计时器,在你的代码块结束的时候停止计时器。

这是一个使用上述代码片段的例子:

我经常将这些计时器的输出记录到文件中,这样就可以观察我的程序的性能如何随着时间进化。

使用分析器逐行统计时间和执行频率

Robert Kern有一个称作line_profiler的不错的项目,我经常使用它查看我的脚步中每行代码多快多频繁的被执行。

想要使用它,你需要通过pip安装该python包:

一旦安装完成,你将会使用一个称做“line_profiler”的新模组和一个“kernprof.py”可执行脚本。

想要使用该工具,首先修改你的源代码,在想要测量的函数上装饰@profile装饰器。不要担心,你不需要导入任何模组。kernprof.py脚本将会在执行的时候将它自动地注入到你的脚步的运行时。

primes.py

一旦你已经设置好了@profile装饰器,使用kernprof.py执行你的脚步。

-l选项通知kernprof注入@profile装饰器到你的脚步的内建函数,-v选项通知kernprof在脚本执行完毕的时候显示计时信息。上述脚本的输出看起来像这样:

寻找具有高Hits值或高Time值的行。这些就是可以通过优化带来最大改善的地方。

程序使用了多少内存?

现在我们对计时有了较好的理解,那么让我们继续弄清楚程序使用了多少内存。我们很幸运,Fabian Pedregosa模仿Robert Kern的line_profiler实现了一个不错的内存分析器。

首先使用pip安装:

(这里建议安装psutil包,因为它可以大大改善memory_profiler的性能)。

就像line_profiler,memory_profiler也需要在感兴趣的函数上面装饰@profile装饰器:

想要观察你的函数使用了多少内存,像下面这样执行:

一旦程序退出,你将会看到看起来像这样的输出:

line_profiler和memory_profiler的IPython快捷方式

memory_profiler和line_profiler有一个鲜为人知的小窍门,两者都有在IPython中的快捷命令。你需要做的就是在IPython会话中输入以下内容:

在这样做的时候你需要访问魔法命令%lprun和%mprun,它们的行为类似于他们的命令行形式。主要区别是你不需要使用@profiledecorator来修饰你要分析的函数。只需要在IPython会话中像先前一样直接运行分析:

这样可以节省你很多时间和精力,因为你的源代码不需要为使用这些分析命令而进行修改。

内存泄漏在哪里?

cPython解释器使用引用计数做为记录内存使用的主要方法。这意味着每个对象包含一个计数器,当某处对该对象的引用被存储时计数器增加,当引用被删除时计数器递减。当计数器到达零时,cPython解释器就知道该对象不再被使用,所以删除对象,释放占用的内存。

如果程序中不再被使用的对象的引用一直被占有,那么就经常发生内存泄漏。

查找这种“内存泄漏”最快的方式是使用Marius Gedminas编写的objgraph,这是一个极好的工具。该工具允许你查看内存中对象的数量,定位含有该对象的引用的所有代码的位置。

一开始,首先安装objgraph:

一旦你已经安装了这个工具,在你的代码中插入一行声明调用调试器:

最普遍的对象是哪些?

在运行的时候,你可以通过执行下述指令查看程序中前20个最普遍的对象:

哪些对象已经被添加或删除?

我们也可以查看两个时间点之间那些对象已经被添加或删除:

谁引用着泄漏的对象?

继续,你还可以查看哪里包含给定对象的引用。让我们以下述简单的程序做为一个例子:

想要看看哪里包含变量x的引用,执行objgraph.show_backref()函数:

该命令的输出应该是一副PNG图像,保存在/tmp/backrefs.png,它看起来是像这样:

在运行的时候,你可以通过执行下述指令查看程序中前20个最普遍的对象:最下面有红字的盒子是我们感兴趣的对象。我们可以看到,它被符号x引用了一次,被列表y引用了三次。如果是x引起了一个内存泄漏,我们可以使用这个方法,通过跟踪它的所有引用,来检查为什么它没有自动的被释放。

回顾一下,objgraph 使我们可以:

显示占据python程序内存的头N个对象

显示一段时间以后哪些对象被删除活增加了

在我们的脚本中显示某个给定对象的所有引用

努力与精度

在本帖中,我给你显示了怎样用几个工具来分析python程序的性能。通过这些工具与技术的武装,你可以获得所有需要的信息,来跟踪一个python程序中大多数的内存泄漏,以及识别出其速度瓶颈。

对许多其他观点来说,运行一次性能分析就意味着在努力目标与事实精度之间做出平衡。如果感到困惑,那么就实现能适应你目前需求的最简单的解决方案。

参考

stack overflow – time explained(堆栈溢出 – 时间解释)

line_profiler(线性分析器)

memory_profiler(内存分析器)

objgraph(对象图)

end

本文分享自微信公众号 - CDA数据分析师(cdacdacda)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-01-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

Python 抓取网页乱码原因分析

在用 python2 抓取网页的时候,经常会遇到抓下来的内容显示出来是乱码。 发生这种情况的最大可能性就是编码问题:运行环境的字符编码和网页的字符编码不一致。 ...

40860
来自专栏PHP在线

小技巧让你的 if else 看起来更漂亮

刚看到一个提问帖: 《如果程序中出现多层嵌套的 if...else...语句,如何重构可使程序逻辑变得更为清晰易读?》,因回答篇幅比较大,单独开个帖子答一下。 ...

29860
来自专栏瓜大三哥

​UVM(九)之sequencej机制续1

UVM(九)之sequencej机制续1 当一个sequence启动起来之后,UVM会自动执行sequence的body任务,所以要产生各种和杨的激励,就要写好...

299100
来自专栏Java技术栈

Java 虚拟机对锁优化所做的努力

作为一款公用平台,JDK 本身也为并发程序的性能绞尽脑汁,在 JDK 内部也想尽一切办法提供并发时的系统吞吐量。这里,我将向大家简单介绍几种 JDK 内部的 "...

8920
来自专栏IMWeb前端团队

【原译】javascript中的错误处理

本文作者:IMWeb ouven 原文出处:IMWeb社区 未经同意,禁止转载 【原译】javascript中的正确错误处理 A Guide to P...

26690
来自专栏Golang语言社区

使用Go语言框架进行web开发笔记

前言 关于golang的web开发有不少框架,例如 martini, gin, revel,gorilla等。 之前玩过revel,感觉封装的太多了,作为一个小...

44070
来自专栏编程

Cgroup原理解释及部署实例(3)

Cgroup设计原理分析 CGroups的源代码较为清晰,我们可以从进程的角度出发来剖析cgroups相关数据结构之间的关系。在Linux中,管理进程的数据结构...

28080
来自专栏Crossin的编程教室

pycharm 如何程序运行后,仍可查看变量值?

来出自知乎同名问题。 因为觉得这个问题挺实用,且确实有很多人不了解,故将自己的回答搬运到这里。 我自己开发时候也经常遇到这样的需求:程序运行后,结果不正确,想要...

61280
来自专栏游戏杂谈

Ant+JSDocTookit生成Javascript文档

需要备上下面三样东西 JSDocTookit http://code.google.com/p/jsdoc-toolkit/

23930
来自专栏逸鹏说道

Python3 与 C# 扩展之~模块专栏

代码裤子:https://github.com/lotapp/BaseCode/tree/maste

15550

扫码关注云+社区

领取腾讯云代金券