如何对我的代码进行性能分析?
我想知道怎么给我的代码做性能分析。
我看过文档,但里面没有例子,所以我没能理解。
我的代码很大,运行起来花了很多时间,所以我想分析一下性能,看看怎么能加快速度。我的代码没有完全用方法写,虽然中间有几个方法,但整体上不是。我代码里也没有主函数。我想知道怎么使用性能分析。
我在找一些示例或者样本代码,看看怎么做性能分析。
我试过用psyco,也就是在我代码的顶部加了两行:
import psyco
psyco.full()
这样做对吗?但是没有看到任何改善。有没有其他加速的方法,麻烦推荐一下。
3 个回答
使用 cProfile。你可以在命令行中使用它,只需把你的模块作为参数传进去,这样就不需要写一个 main
方法了。
编辑:
这个答案已经在 https://github.com/campos-ddc/cprofile_graph 上实现了。
使用 cProfile 进行性能分析
这是我之前写的一篇关于使用 cProfile 进行性能分析的文章,里面有一些图形辅助。
cProfile 是 Python 中最常用的性能分析工具之一,虽然功能强大,但标准的文本输出看起来有点单调。在这里,我将向你展示如何更简单地在你的应用程序中使用 cProfile。
使用 cProfile 有两种常见的方法:你可以在命令行中使用它来分析一个模块,或者在你的代码中使用它,分析特定的代码片段。
分析一个模块
要使用 cProfile 分析整个模块,只需在命令行中输入以下命令:
python -m cProfile -o output_filename.pstats path/to/script arg1 arg2
这将使用给定的参数(可选)运行你的模块,并将输出结果保存到 output_filename.pstats 文件中。
有很多方法可以查看这个输出文件中的数据,但为了这篇文章,我们不必担心这些,重点是如何得到图形化的可视化效果。
从内部分析
有时候你并不想分析整个模块,只想分析其中的几行代码。
为此,你需要在你的模块中添加一些代码。
首先:
import cProfile
然后,你可以用以下代码替换任何代码段:
cProfile.runctx('Your code here', globals(), locals(), 'output_file')
例如,下面是分析前后的测试:
import unittest
class Test(unittest.TestCase):
def testSomething(self):
self.DoSomethingIDontCareAbout()
param = 'whatever'
self.RunFunctionIThinkIsSlow(param)
self.AssertSomeStuff() # This is after all, a test
分析后:
import unittest
import cProfile
class Test(unittest.TestCase):
def testSomething(self):
self.DoSomethingIDontCareAbout()
param = 'whatever'
cProfile.runctx(
'self.RunFunctionIThinkIsSlow(param)',
globals(),
locals(),
'myProfilingFile.pstats'
)
self.AssertSomeStuff() # This is after all, a test
将 pstats 文件转换为图形
要将你的性能分析文件转换为图形,你需要准备几个东西:
下载并安装 gprof2dot 和 GraphViz 后,在命令行中运行以下命令:
python gprof2dot -f pstats myProfileFile | dot -Tpng -o image_output.png
你可能需要为 gprof2dot 和/或 dot 使用完整路径,或者可以将它们添加到你的 PATH 环境变量中。
完成这些步骤后,你应该会得到一张看起来像这样的图像:
颜色越热(红色、橙色、黄色)表示这个函数占用了更多的总运行时间,而颜色越冷(绿色、蓝色)则表示占用的时间较少。
在每个节点上,你可以看到这个函数占用了总运行时间的百分比,以及它被调用的次数。
节点之间的箭头表示哪个函数调用了其他函数,这些箭头上也会标注通过这个调用占用了多少运行时间的百分比。
注意:百分比不一定会加起来等于 100%,特别是在引用 C++ 代码的代码段中,因为这些部分不会被分析。cProfile 也无法确定在 "eval" 语句内部调用了什么,所以你可能会在图中看到一些跳跃。
这个问题的标准答案是使用 cProfile。
不过你会发现,如果你的代码没有分成不同的方法,cProfile 提供的信息就不会特别丰富。
相反,你可以试试另一位网友提到的 蒙特卡洛分析。引用自 另一个答案:
如果你很着急,并且可以在程序运行得很慢的时候手动中断程序,那么有一个简单的方法可以找到性能问题。
只需多次暂停程序,每次查看调用栈。如果有某段代码浪费了20%、50%或其他比例的时间,那么在每次取样时,你都有可能抓到它。这大致就是你能看到它的比例。你不需要进行复杂的猜测。如果你对问题有个大概的想法,这个方法可以验证你的猜测。
你可能会遇到多个不同大小的性能问题。如果你解决了其中一个,剩下的问题所占的比例会变大,更容易被发现。
注意:程序员通常对这种方法持怀疑态度,除非他们自己用过。他们会说分析工具能提供这些信息,但这只有在它们采样整个调用栈时才成立。调用图不能提供相同的信息,因为1)它们不在指令级别进行总结,2)在递归的情况下,它们会给出混乱的总结。他们还会说这种方法只适用于简单的程序,但实际上它适用于任何程序,而且在大程序上效果更好,因为它们通常有更多的问题可以发现 [强调补充]。
这不是传统的方法,但我在一个项目中成功使用过它,当时使用 cProfile 的分析结果并没有给我有用的信息。
它最好的地方在于,这在 Python 中非常简单。只需在解释器中运行你的 Python 脚本,按下 [Control-C],记录下回溯信息,然后重复几次。