跳转到主要内容

逐行分析器

项目描述

Pypi ReadTheDocs Downloads CircleCI GithubActions Codecov

这是官方的 line_profiler 仓库。pypi 上 line-profiler 的最新版本指向这个仓库。由 @rkern 开发的原始 line_profiler 包已经不再维护。这个分支是项目的官方延续。

Github

https://github.com/pyutils/line_profiler

Pypi

https://pypi.ac.cn/project/line_profiler

ReadTheDocs

https://kernprof.readthedocs.io/en/latest/


line_profiler 是一个用于函数逐行分析的模块。kernprof 是一个方便的脚本,用于运行 line_profiler 或 Python 标准库的 cProfile 或 profile 模块,具体取决于可用性。

它们在 BSD 许可证 下可用。

快速入门(现代版)

本指南适用于从 4.1.0 版本开始的 line profiler。

分析 Python 脚本

  • 安装 line_profiler: pip install line_profiler

  • 在相关文件中,导入 line_profiler 并使用 @line_profiler.profile 装饰您想要分析的函数。

  • 设置环境变量 LINE_PROFILE=1 并像平常一样运行您的脚本。脚本结束时,将输出分析结果的摘要、写入磁盘的文件以及检查详细信息的说明。

有关更多详细信息,请参阅Line Profiler 基本用法

快速入门(旧版)

本节是原始快速入门指南,最终可能从 README 中删除。它适用于当前版本和旧版本(4.1.0 之前)的 line profiler。

分析 Python 脚本

  • 安装 line_profiler: pip install line_profiler

  • 使用 @profile 装饰您想要分析的函数。装饰器将在运行时自动可用。

  • 运行 kernprof -lv script_to_profile.py

安装

可以使用 pip 安装 line_profiler 的版本。

$ pip install line_profiler

在确保安装兼容的 IPython 版本的同时,也可以使用 pip 安装。

$ pip install line_profiler[ipython]

要检查开发源代码,您可以使用 Git

$ git clone https://github.com/pyutils/line_profiler.git

您也可以从该 URL 下载任何快照的源代码 tar 包。

源代码发布需要 C 编译器才能构建 line_profiler。此外,git 检出还需要 Cython。PyPI 上的源代码发布应包含预生成的 C 源代码,因此在这种情况下不需要 Cython。

kernprof 是一个单文件纯 Python 脚本,不需要编译器。如果您希望使用它来运行 cProfile 而不是逐行分析,您可以手动将其复制到您的 PATH 目录中,并避免尝试构建任何 C 扩展。

截至 2021-06-04,Linux (x86_64 和 i686)、OSX (10_9_x86_64) 和 Win32 (win32 和 amd64) 的二进制文件在 pypi 上可用。

支持 Python 2.7 的最后版本是 line_profiler 的 3.1.0,支持 Python 3.5 的最后版本是 3.3.1。

line_profiler

Python当前支持的性能分析工具只能计时函数调用。这是定位程序中热点的一种很好的第一步,通常只需要这样做就可以优化程序。然而,有时热点的原因实际上是在函数中的一行代码,而这一行代码仅从阅读源代码中可能并不明显。这些情况在科学计算中尤其常见。函数通常更大(有时因为合法的算法复杂性,有时因为程序员仍在尝试编写FORTRAN代码),在没有函数调用的情况下,使用numpy等库时,单条语句可能会触发大量的计算。但是,cProfile只计时显式的函数调用,而不是由于语法而调用的特殊方法。因此,对于这样的大型数组,numpy的相对较慢的操作,

a[large_index_array] = some_other_large_array

是cProfile无法将其分解的热点,因为该语句中没有显式的函数调用。

LineProfiler可以指定要分析的功能,并将计算每个函数内部每条单独语句的执行时间。在典型的流程中,人们只关心少数几个函数的行计时,因为查看每行代码的计时结果会令人难以承受。然而,LineProfiler确实需要明确告知要分析哪些函数。开始的最简单方法是使用kernprof脚本。

$ kernprof -l script_to_profile.py

kernprof会创建一个LineProfiler实例并将其插入到__builtins__命名空间中,名称为profile。它被编写为用作装饰器,因此在你的脚本中,你可以使用@profile装饰器来装饰你想要分析的功能。

@profile
def slow_function(a, b, c):
    ...

kernprof的默认行为是将结果放入二进制文件script_to_profile.py.lprof中。你可以使用[-v/–view]选项告诉kernprof立即在终端查看格式化的结果。否则,你可以稍后按如下方式查看结果

$ python -m line_profiler script_to_profile.py.lprof

例如,以下是分析pystone.py基准测试(装饰版)中的单个函数的结果(前两行是来自pystone.py的输出,不是来自kernprof

Pystone(1.1) time for 50000 passes = 2.48
This machine benchmarks at 20161.3 pystones/second
Wrote profile results to pystone.py.lprof
Timer unit: 1e-06 s

File: pystone.py
Function: Proc2 at line 149
Total time: 0.606656 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   149                                           @profile
   150                                           def Proc2(IntParIO):
   151     50000        82003      1.6     13.5      IntLoc = IntParIO + 10
   152     50000        63162      1.3     10.4      while 1:
   153     50000        69065      1.4     11.4          if Char1Glob == 'A':
   154     50000        66354      1.3     10.9              IntLoc = IntLoc - 1
   155     50000        67263      1.3     11.1              IntParIO = IntLoc - IntGlob
   156     50000        65494      1.3     10.8              EnumLoc = Ident1
   157     50000        68001      1.4     11.2          if EnumLoc == Ident1:
   158     50000        63739      1.3     10.5              break
   159     50000        61575      1.2     10.1      return IntParIO

函数的源代码与每行的计时信息一起打印出来。这里有六列信息。

  • 行号:文件中的行号。

  • 命中次数:该行被执行的次数。

  • 时间:执行该行所花费的总时间(计时器的单位)。在表格前面的标题信息中,你会看到“计时器单位:”行,给出转换为秒的转换因子。在不同的系统上可能会有所不同。

  • 每次命中:执行该行一次的平均时间(计时器的单位)。

  • 时间百分比:相对于记录的函数总耗时,该行的耗时百分比。

  • 行内容:实际的源代码。请注意,当查看格式化的结果时,这始终是从磁盘读取的,而不是代码执行时。如果你在此期间编辑了文件,则行将不会匹配,格式化器甚至可能无法定位到要显示的功能。

如果你使用IPython,有一个名为%lprun的魔法命令的实现,可以让你指定要分析的功能和要执行的语句。它也会将其LineProfiler实例添加到__builtins__中,但通常你不会这样使用它。

对于IPython 0.11+,你可以通过编辑IPython配置文件~/.ipython/profile_default/ipython_config.py来安装它,向扩展列表中添加'line_profiler'

c.TerminalIPythonApp.extensions = [
    'line_profiler',
]

或显式调用

%load_ext line_profiler

要获取%lprun的用法帮助,请使用标准的IPython帮助机制

In [1]: %lprun?

这两种方法预计将是使用LineProfiler最频繁的用户级方式,通常也最容易。然而,如果您正在使用LineProfiler构建其他工具,您将需要使用API。有两种方式通知LineProfiler要分析的函数:您可以将它们作为构造函数的参数传递,或在实例化后使用 add_function(f) 方法。

profile = LineProfiler(f, g)
profile.add_function(h)

LineProfiler具有与cProfile.Profile相同的 run()runctx()runcall() 方法,以及 enable()disable()。但是需要注意的是,enable()disable() 在嵌套时并不完全安全。在使用LineProfiler作为装饰器时,嵌套是很常见的。为了支持嵌套,请使用 enable_by_count()disable_by_count()。这些函数将增加和减少一个计数器,并且仅在计数器从0过渡到或从0过渡时才实际启用或禁用分析器。

分析后,dump_stats(filename) 方法将结果序列化到指定的文件中。 print_stats([stream]) 将格式化的结果打印到sys.stdout或您指定的任何流。 get_stats() 将返回LineStats对象,它仅包含两个属性:一个包含结果的字典和计时器单位。

kernprof

kernprof 也可以与cProfile、其第三方版本lsprof或纯Python的profile模块一起使用,具体取决于可用的内容。它有几个主要功能

  • 封装分析关注点。您不需要修改您的脚本就可以开始分析并保存结果。除非您想使用高级的 __builtins__ 功能。

  • 健壮的脚本执行。许多脚本需要将 __name__、__file__ 和 sys.path 设置为相对于它。在封装时,使用 execfile() 可能是天真的一种方法,但许多依赖于该信息的脚本将会失败。kernprof将在执行脚本之前正确设置这些变量。

  • 易于可执行位置。如果您正在分析安装在您的PATH中的应用程序,您可以直接给出可执行文件名。如果kernprof在当前目录中找不到指定的脚本,它将在您的PATH中查找它。

  • 将分析器插入到 __builtins__ 中。有时,您只想分析您代码的一小部分。使用 [-b/–builtin] 参数,分析器将被实例化并插入到您的 __builtins__ 中,名称为“profile”。像LineProfiler一样,它可以作为装饰器使用,或者使用 enable_by_count()disable_by_count() 启用/禁用,甚至可以作为上下文管理器使用“with profile:”语句。

  • 预分析设置。使用 [-s/–setup] 选项,您可以在执行主脚本之前执行一个不进行分析的脚本。这在大型库(如wxPython或VTK)的导入干扰结果的情况下非常有用。如果可以修改源代码,__builtins__ 方法可能更简单。

默认情况下,profile脚本脚本_to_profile.py 的结果将写入脚本_to_profile.py.prof。它将是一个典型的序列化文件,可以使用pstats.Stats()读取。它们可以通过以下命令交互式查看

$ python -m pstats script_to_profile.py.prof

这些文件也可以使用图形工具查看。以下是基于 cProfileline_profiler 构建的第三方工具列表

常见问题解答

  • 为什么叫“kernprof”?

    我没能想出一个有意义的名字,所以以自己的名字命名。

  • 当一个被分析函数调用另一个函数时,按行计时的结果并不相加。这是怎么回事?

    假设你有函数F()调用函数G(),并且你在两个函数上使用了LineProfiler。G()报告的总时间小于F()中调用G()的行报告的时间。原因是我记录时间的方式相当聪明(也可能太聪明了)。基本上,我试图防止记录LineProfiler为每一行做的所有记录时间。每当Python的跟踪设施发出一行事件(这发生在实际执行行之前),LineProfiler会找到两个时间戳,一个在执行任何操作之前(t_begin),一个尽可能接近结束(t_end)。LineProfiler数据结构的几乎所有开销都发生在这两个时间之间。

    当一个行事件到来时,LineProfiler会找到它所属的函数。如果是该函数的第一行,我们会记录该函数的行号和与该函数关联的t_end。下次我们看到属于该函数的行事件时,我们会取新事件的t_begin并从旧t_end中减去,以找到在旧行上花费的时间。然后我们将新的t_end记录为该函数的当前活动行。这样,我们就从结果中移除了大部分LineProfiler的开销。几乎是这样。当一个被分析函数F调用另一个被分析函数G时,F中调用G的行实际上记录了执行该行所花费的总时间,这包括在G内部调用分析器所花费的时间。

    当这个问题第一次被问起时,提问者将G()函数调用作为更大表达式的一部分,并且他想尝试估计在函数中花费的时间与表达式中其他部分花费的时间的对比。我的回答是,即使我可以移除影响,这仍然可能是有误导性的。G()可能在F()的相关行之外的其他地方被调用。解决方案是将代码修改为将其拆分为两行,一行将G()的结果赋值给一个临时变量,另一行包含剩余的表达式。

    我欢迎有关如何使这更稳健的建议。或者简单的警告,反对试图变得太聪明。

  • 为什么我在使用LineProfiler时,列表推导式有如此多的匹配项?

    LineProfiler会为列表推导式的每一轮迭代记录一次包含列表推导式的行。

  • 为什么kernprof与line_profiler一起分发?它不是只用cProfile就可以工作吗?

    部分原因是kernprof.py对于有效地使用line_profiler是必不可少的,但更多的是因为我懒惰,不想维护如此小的模块的额外开销。然而,kernprof.py是一个独立的、纯Python脚本,可以仅使用Python标准库来进行函数性能分析。您可以单独获取并安装它,而不需要line_profiler

  • 构建line_profiler和kernprof.py需要C编译器吗?

    您确实需要C编译器来构建line_profiler。kernprof.py是一个纯Python脚本,可以单独安装。

  • 构建line_profiler需要Cython吗?

    PyPI上提供了支持Python版本的车轮,支持linux、osx和windows的x86-64架构。Linux还包含许多linux和musllinux的i686车轮。如果您有不同的CPU架构或不受支持的Python版本,则需要从源代码构建。

  • 我需要什么版本的Python?

    line_profilerkernprof已经与Python 3.6-3.11进行了测试。较老的line_profiler版本支持较老的Python版本。

待办事项

cProfile使用一个巧妙的“旋转树”数据结构来最小化查找和记录条目的开销。LineProfiler由于Cython而使用Python字典和扩展对象。这最初是一个我想尽快玩玩的原型,所以我暂时放弃了旋转树。像往常一样,我让它工作了,并且它似乎具有可接受的性能,所以我现在不太想使用不同的策略。也许以后。欢迎贡献!

错误和类似问题

可以在GitHub上提交错误和pull请求。

变更

请参阅CHANGELOG

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

line_profiler-4.1.3.tar.gz (196.8 kB 查看散列)

上传时间

构建分布

line_profiler-4.1.3-cp312-cp312-win_amd64.whl (126.6 kB 查看散列)

上传时间 CPython 3.12 Windows x86-64

line_profiler-4.1.3-cp312-cp312-musllinux_1_1_x86_64.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.12 musllinux: musl 1.1+ x86_64

line_profiler-4.1.3-cp312-cp312-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.12 musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (720.6 kB 查看哈希值)

上传时间: CPython 3.12 manylinux: glibc 2.17+ x86_64

line_profiler-4.1.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl (692.0 kB 查看哈希值)

上传时间: CPython 3.12 manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp312-cp312-macosx_11_0_arm64.whl (132.6 kB 查看哈希值)

上传时间: CPython 3.12 macOS 11.0+ ARM64

line_profiler-4.1.3-cp312-cp312-macosx_10_9_x86_64.whl (139.9 kB 查看哈希值)

上传时间: CPython 3.12 macOS 10.9+ x86_64

line_profiler-4.1.3-cp312-cp312-macosx_10_9_universal2.whl (219.1 kB 查看哈希值)

上传时间: CPython 3.12 macOS 10.9+ universal2 (ARM64, x86-64)

line_profiler-4.1.3-cp311-cp311-win_amd64.whl (126.6 kB 查看哈希值)

上传时间: CPython 3.11 Windows x86-64

line_profiler-4.1.3-cp311-cp311-musllinux_1_1_x86_64.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.11 musllinux: musl 1.1+ x86_64

line_profiler-4.1.3-cp311-cp311-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.11 musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (748.4 kB 查看哈希值)

上传时间 CPython 3.11 manylinux: glibc 2.17+ x86-64

line_profiler-4.1.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl (728.0 kB 查看哈希值)

上传时间 CPython 3.11 manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp311-cp311-macosx_11_0_arm64.whl (132.8 kB 查看哈希值)

上传时间 CPython 3.11 macOS 11.0+ ARM64

line_profiler-4.1.3-cp311-cp311-macosx_10_9_x86_64.whl (139.8 kB 查看哈希值)

上传时间 CPython 3.11 macOS 10.9+ x86-64

line_profiler-4.1.3-cp311-cp311-macosx_10_9_universal2.whl (219.2 kB 查看哈希值)

上传时间 CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64)

line_profiler-4.1.3-cp310-cp310-win_amd64.whl (125.5 kB 查看哈希值)

上传时间 CPython 3.10 Windows x86-64

line_profiler-4.1.3-cp310-cp310-musllinux_1_1_x86_64.whl (1.3 MB 查看哈希值)

上传时间 CPython 3.10 musllinux: musl 1.1+ x86-64

line_profiler-4.1.3-cp310-cp310-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间 CPython 3.10 musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (717.6 kB 查看哈希值)

上传时间 CPython 3.10 manylinux: glibc 2.17+ x86-64

line_profiler-4.1.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl (699.3 kB 查看哈希值)

上传于 CPython 3.10 manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp310-cp310-macosx_11_0_arm64.whl (133.2 kB 查看哈希值)

上传于 CPython 3.10 macOS 11.0+ ARM64

line_profiler-4.1.3-cp310-cp310-macosx_10_9_x86_64.whl (139.8 kB 查看哈希值)

上传于 CPython 3.10 macOS 10.9+ x86-64

line_profiler-4.1.3-cp310-cp310-macosx_10_9_universal2.whl (219.5 kB 查看哈希值)

上传于 CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64)

line_profiler-4.1.3-cp39-cp39-win_amd64.whl (126.0 kB 查看哈希值)

上传于 CPython 3.9 Windows x86-64

line_profiler-4.1.3-cp39-cp39-musllinux_1_1_x86_64.whl (1.3 MB 查看哈希值)

上传于 CPython 3.9 musllinux: musl 1.1+ x86-64

line_profiler-4.1.3-cp39-cp39-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传于 CPython 3.9 musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (718.1 kB 查看哈希值)

上传于 CPython 3.9 manylinux: glibc 2.17+ x86-64

line_profiler-4.1.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl (699.0 kB 查看哈希值)

上传于 CPython 3.9 manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp39-cp39-macosx_11_0_arm64.whl (133.6 kB 查看哈希值)

上传于 CPython 3.9 macOS 11.0+ ARM64

line_profiler-4.1.3-cp39-cp39-macosx_10_9_x86_64.whl (140.3 kB 查看哈希值)

上传于 CPython 3.9 macOS 10.9+ x86-64

line_profiler-4.1.3-cp39-cp39-macosx_10_9_universal2.whl (220.5 kB 查看哈希值)

上传时间 CPython 3.9 macOS 10.9+ universal2 (ARM64, x86-64)

line_profiler-4.1.3-cp38-cp38-win_amd64.whl (126.4 kB 查看哈希值)

上传时间 CPython 3.8 Windows x86-64

line_profiler-4.1.3-cp38-cp38-musllinux_1_1_x86_64.whl (1.3 MB 查看哈希值)

上传时间 CPython 3.8 musllinux: musl 1.1+ x86-64

line_profiler-4.1.3-cp38-cp38-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间 CPython 3.8 musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (728.3 kB 查看哈希值)

上传时间 CPython 3.8 manylinux: glibc 2.17+ x86-64

line_profiler-4.1.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl (710.8 kB 查看哈希值)

上传时间 CPython 3.8 manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp38-cp38-macosx_11_0_arm64.whl (133.7 kB 查看哈希值)

上传时间 CPython 3.8 macOS 11.0+ ARM64

line_profiler-4.1.3-cp38-cp38-macosx_10_9_x86_64.whl (140.4 kB 查看哈希值)

上传时间 CPython 3.8 macOS 10.9+ x86-64

line_profiler-4.1.3-cp38-cp38-macosx_10_9_universal2.whl (220.6 kB 查看哈希值)

上传时间 CPython 3.8 macOS 10.9+ universal2 (ARM64, x86-64)

line_profiler-4.1.3-cp37-cp37m-win_amd64.whl (122.7 kB 查看哈希值)

上传时间 CPython 3.7m Windows x86-64

line_profiler-4.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl (1.2 MB 查看哈希值)

上传时间: CPython 3.7m musllinux: musl 1.1+ x86_64

line_profiler-4.1.3-cp37-cp37m-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.7m musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (681.5 kB 查看哈希值)

上传时间: CPython 3.7m manylinux: glibc 2.17+ x86_64

line_profiler-4.1.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl (665.1 kB 查看哈希值)

上传时间: CPython 3.7m manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp37-cp37m-macosx_10_9_x86_64.whl (136.6 kB 查看哈希值)

上传时间: CPython 3.7m macOS 10.9+ x86_64

line_profiler-4.1.3-cp36-cp36m-win_amd64.whl (130.3 kB 查看哈希值)

上传时间: CPython 3.6m Windows x86-64

line_profiler-4.1.3-cp36-cp36m-musllinux_1_1_x86_64.whl (1.2 MB 查看哈希值)

上传时间: CPython 3.6m musllinux: musl 1.1+ x86_64

line_profiler-4.1.3-cp36-cp36m-musllinux_1_1_i686.whl (1.3 MB 查看哈希值)

上传时间: CPython 3.6m musllinux: musl 1.1+ i686

line_profiler-4.1.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (670.3 kB 查看哈希值)

上传时间: CPython 3.6m manylinux: glibc 2.17+ x86_64

line_profiler-4.1.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl (652.1 kB 查看哈希值)

上传时间: CPython 3.6m manylinux: glibc 2.17+ i686

line_profiler-4.1.3-cp36-cp36m-macosx_10_9_x86_64.whl (133.3 kB 查看哈希值)

上传于 CPython 3.6m macOS 10.9+ x86-64

支持者