Python性能分析(2)-line_profiler
本文最后更新于 2023年3月6日 下午
简介
line_profiler 包提供了对函数运行时间逐行分析的功能。原作者为 Robert Kern,目前由社区组织 OpenPyUtils 维护更新。
本文大部分内容来自于对 line_profiler 文档的中文翻译,旨在向更多中文用户推广该工具。
快速上手
使用 line_profiler 分析函数性能只需要三步:
- 通过pip安装:
pip install line_profiler
- 在需要被分析的函数上添加
@profile
装饰器 - 运行
kernprof -lv script_to_profile.py
安装
可以使用 pip 安装 line_profiler 的发行版:
1 |
|
也可以通过 pip 安装适用于 IPython 的版本(注意版本间的兼容问题):
1 |
|
可以使用 Git 查看开发源码:
1 |
|
line_profiler
当前 Python 中支持的分析工具只计量函数调用时间。这是在程序中定位热点的良好开始,经常也是优化程序所需要做的全部工作。然而,有时热点仅仅来自于函数中的某一行,只依靠阅读源码很难发现。这类情况在科学计算中尤为常见。科学计算中的函数往往更大(因为合理算法的复杂性,或因为程序员仍然在尝试编写 FORTRAN 代码),以及在函数体之外的使用 numpy 等库的单个语句会触发大量计算。cProfile 只计时显式函数调用,而非因语法调用的特殊方法。因此,像这样的在大型数组上使用相对较慢的 numpy 操作,
1 |
|
是一个永远不会被 cProfile 打破的 hotspot,因为该语句中并没有显式函数调用。
向 LineProfiler 中传入函数以进行分析,它将会计量这些函数中每一行执行的耗时。在典型工作流中,人们只关心个别几个函数的行时间,因为梳理每一行代码的计时结果会让人不知所措。而 LineProfiler 的确需要被明确告知要对哪些函数进行分析。最简单的方法是使用 kernprof 脚本。
1 |
|
kernprof 将创建一个名为 profile 的 LineProfiler 实例,并将其插入 __builtins__
命名空间。它被当作装饰器使用,所以在你的脚本中,使用 @profile
来装饰想要分析的函数。
1 |
|
kernprof 的默认行为是将结果存储到二进制文件 script_to_profile.py.lprof
中。可以使用 [-v/--view]
选项告知 kernprof 立即在终端显示格式化的结果。或者,可以像这样稍后查看结果:
1 |
|
例如,下面是对带有装饰器版本的 pystone.py
benchmark 中的一个函数进行分析的结果(前两行是 pystone.py
的输出,而非 kernprof):
1 |
|
逐行显示出了函数的源码及其耗时信息。共有六列信息。
- Line #: 在文件中的行号
- Hits: 该行被执行的次数
- Time: 执行该行的总时间,以定时器为单位表示。在表格前的标题信息中,有一行“Timer unit(定时器单位)”,给出了与秒的换算关系。在不同的系统上可能有所不同。
- Per Hit: 以定时器为单位,执行一次该行的平均时间
- % Time: 该行所花费的时间,在该函数的总记录时间中所占的百分比
- Line Contents: 实际的源码。注意这是在查看格式化结果时从磁盘上读取的,而不是在执行时获取。如果在执行测试与格式化查看之间编辑了源码文件,这些行就会不一致,格式化器甚至可能无法找到要显示的函数
kernprof
kernprof 也可用于 cProfile
、其第三方实现 Isprof
或纯 Python profile
模块。它有如下几个特性:
- 对性能分析进行封装。无需为了启动分析与保存结果而修改原有的脚本。当然,想要使用更高级的
__builtins__
功能除外。 - 高鲁棒性的脚本运行能力。许多脚本需要使用
__name__
、__file__
、sys.path
等来处理相对路径。如果简单地只使用execfile()
来进行封装,则许多依赖这些信息的脚本会失效。kernprof 会在执行脚本前正确处理这些变量。 - 简单的可执行程序位置。如果要对安装在 PATH 上的应用程序进行性能分析,可以直接使用其名称。如果 kernprof 在当前目录下没有找到指定的脚本,则会在 PATH 中搜索。
- 将性能分析器插入到
__builtins__
中。有时只想对代码的一小部分进行分析,则可以使用[-b/--builtin]
选项,分析器将以 “profile” 的名称被实例化并插入到__builtins__
中。像 LineProfiler 一样,它可以作为装饰器使用,或者通过 enable_by_count() 和 disable_by_count() 启用/禁用,甚至可以通过 “with profile:” 语句作为上下文管理器使用 - 设置分析准备。使用
[-s/--setup]
选项,可以在执行主脚本前,以不做分析的方式运行一个指定脚本。这通常适用于像 wxPython 或 VTK 这样的大型库的导入对结果产生干扰的情况。如果可以修改源码,使用__builtins__
的方式可能更容易。
分析 script_to_profile.py
的结果默认写入 script_to_profile.py.prof
中。它是一个典型的 marshalled 文件,可以用 pstats.Starts()
读取。可以用命令进行交互式查看:
1 |
|
这些文件也可以用图形化工具来查看。基于 cProfile
或 line_profiler
的第三方工具列表如下: