【问题标题】:How do I use line_profiler (from Robert Kern)?如何使用 line_profiler(来自 Robert Kern)?
【发布时间】:2014-07-16 02:44:18
【问题描述】:

我尝试使用 line_profiler 模块来获取 Python 文件的逐行配置文件。这是我到目前为止所做的:

1) 使用 .exe 文件从pypi 安装 line_profiler(我在 WinXP 和 Win7 上)。只需点击安装向导即可。

2) 编写一小段代码(类似于在另一个已回答的问题here 中提出的问题)。

from line_profiler import LineProfiler
def do_stuff(numbers):
    print numbers

numbers = 2
profile = LineProfiler(do_stuff(numbers))
profile.print_stats()

3) 从 IDLE/PyScripter 运行代码。我只有时间。

Timer unit: 4.17188e-10 s

如何获得我执行的代码的完整的逐行配置文件?我从未使用过任何高级 Python 功能,例如装饰器,因此我很难理解如何使用 herehere 等几篇帖子提供的指南。

【问题讨论】:

    标签: python python-2.7 profiling line-profiler


    【解决方案1】:

    发现 line_profiler 使用装饰器很好用,即 @profile 对我有用:

    def profile(func):
        from functools import wraps
    
        @wraps(func)
        def wrapper(*args, **kwargs):
            from line_profiler import LineProfiler
            prof = LineProfiler()
            try:
                return prof(func)(*args, **kwargs)
            finally:
                prof.print_stats()
    
        return wrapper
    

    致谢:pavelpatrin

    【讨论】:

      【解决方案2】:

      加载 line_profiler 和 numpy

      %load_ext line_profiler
      import numpy as np
      

      定义一个函数例如:

      def take_sqr(array):
          sqr_ar = [np.sqrt(x) for x in array]
          return sqr_ar
      

      使用line_profiler计算时间如下:

      %lprun -f take_sqr take_sqr([1,2,3])
      

      输出如下所示:

      Timer unit: 1e-06 s
       
      Total time: 6e-05 s File: <ipython-input-5-e50c1b05a473> Function:
      take_sqr at line 1
      
      Line #      Hits         Time  Per Hit   % Time  Line Contents
      ==============================================================
           1                                           def take_sqr(array):
           2         4         59.0     14.8     98.3      sqr_ar = [np.sqrt(x) for x in array]
           3         1          1.0      1.0      1.7      return sqr_ar
      

      【讨论】:

      • 欢迎来到 Stack Overflow!纯代码答案并不是特别有用。请简要说明此代码如何解决问题。
      • 谢谢!我刚刚编辑了回复。我希望你现在发现它更清楚了。
      【解决方案3】:

      如果你使用 PyCharm,你也可以看看 https://plugins.jetbrains.com/plugin/16536-line-profiler

      这是我创建的一个插件,可让您将线分析器结果加载并可视化到 PyCharm 编辑器中。

      【讨论】:

        【解决方案4】:

        只需按照第一个link 中的 Dan Riti 示例,但使用您的代码。安装 line_profiler 模块后,您所要做的就是在您希望逐行分析的每个函数之前添加一个 @profile 装饰器,并确保每个函数在代码中的其他位置至少调用一次 - 所以对于您的简单示例代码将是这样的:

        example.py文件:

        @profile
        def do_stuff(numbers):
            print numbers
        
        numbers = 2
        do_stuff(numbers)
        

        完成后,通过安装在您的C:\Python27\Scripts 目录中的kernprof.py 运行您的脚本。这是在 Windows 7 命令行会话中执行此操作的(不是很有趣的)实际输出:

        > python "C:\Python27\Scripts\kernprof.py" -l -v example.py
        2
        Wrote profile results to example.py.lprof
        Timer unit: 3.2079e-07 s
        
        File: example.py
        Function: do_stuff at line 2
        Total time: 0.00185256 s
        
        Line #      Hits         Time  Per Hit   % Time  Line Contents
        ==============================================================
             1                                           @profile
             2                                           def do_stuff(numbers):
             3         1         5775   5775.0    100.0      print numbers
        

        您可能需要调整最后一步 - 使用 kernprof.py 而不是直接由 Python 解释器运行测试脚本 - 以便在 IDLE 或 PyScripter 中执行等效操作。

        更新

        似乎在line_profiler v1.0 中,kernprof 实用程序是作为可执行文件分发的,而不是像我编写上述代码时那样的.py 脚本文件。这意味着现在需要使用以下命令从命令行调用它:

        > "C:\Python27\Scripts\kernprof.exe" -l -v example.py
        

        【讨论】:

        • 不客气。顺便说一句,如果您想正常运行脚本(没有 kernprof.py),您需要删除 @profile 装饰器调用或定义自己的虚拟调用:例如profile = lambda f: f 在文件的开头。
        • 这对我不起作用,因为我似乎无法调用 kernprof.py。我使用 pip install 来获取 line_profiler 并且有一个名为 kernprof 的文件,但它没有 .py 扩展名,你知道为什么会这样吗?谢谢。
        • @Alex:不知道......可能是一个糟糕的安装。再试一次,如果没有pip,从pypi 下载模块的源代码,手动操作是行不通的。
        • @Alex kernprof 不必具有 .py 扩展名才能执行。我的安装在 venv 内,运行良好,就像 kernprof 一样。
        • @martineau 你知道为什么原始代码没有收集统计​​信息吗?这是一个最小的例子,调用对我来说“看起来不错”。当将LineProfiler(..) 替换为cProfile.Profile(...) 时,似乎运行良好。
        【解决方案5】:

        这个答案是我的答案 here 的副本,关于如何从 Python 脚本中获取 line_profiler 统计信息(无需从命令行使用 kernprof 或必须将 @profile 装饰器添加到函数和类方法中)。类似line_profiler 问题的所有答案(我见过)仅使用kernprof 进行描述。


        line_profiler 测试用例(在GitHub 上找到)有一个示例,说明如何从 Python 脚本中生成配置文件数据。您必须包装要分析的函数,然后调用包装器传递任何所需的函数参数。

        from line_profiler import LineProfiler
        import random
        
        def do_stuff(numbers):
            s = sum(numbers)
            l = [numbers[i]/43 for i in range(len(numbers))]
            m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        
        numbers = [random.randint(1,100) for i in range(1000)]
        lp = LineProfiler()
        lp_wrapper = lp(do_stuff)
        lp_wrapper(numbers)
        lp.print_stats()
        

        输出:

        Timer unit: 1e-06 s
        
        Total time: 0.000649 s
        File: <ipython-input-2-2e060b054fea>
        Function: do_stuff at line 4
        
        Line #      Hits         Time  Per Hit   % Time  Line Contents
        ==============================================================
             4                                           def do_stuff(numbers):
             5         1           10     10.0      1.5      s = sum(numbers)
             6         1          186    186.0     28.7      l = [numbers[i]/43 for i in range(len(numbers))]
             7         1          453    453.0     69.8      m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        

        向配置文件添加附加功能

        此外,您还可以添加要分析的其他功能。例如,如果您有第二个 calling 函数并且您只包装了 calling 函数,那么您将只能看到 calling 的配置文件结果功能。

        from line_profiler import LineProfiler
        import random
        
        def do_other_stuff(numbers):
            s = sum(numbers)
        
        def do_stuff(numbers):
            do_other_stuff(numbers)
            l = [numbers[i]/43 for i in range(len(numbers))]
            m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        
        numbers = [random.randint(1,100) for i in range(1000)]
        lp = LineProfiler()
        lp_wrapper = lp(do_stuff)
        lp_wrapper(numbers)
        lp.print_stats()
        

        以上只会为调用函数产生以下配置文件输出:

        Timer unit: 1e-06 s
        
        Total time: 0.000773 s
        File: <ipython-input-3-ec0394d0a501>
        Function: do_stuff at line 7
        
        Line #      Hits         Time  Per Hit   % Time  Line Contents
        ==============================================================
             7                                           def do_stuff(numbers):
             8         1           11     11.0      1.4      do_other_stuff(numbers)
             9         1          236    236.0     30.5      l = [numbers[i]/43 for i in range(len(numbers))]
            10         1          526    526.0     68.0      m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        

        在这种情况下,您可以像这样将附加的 调用 函数添加到配置文件中:

        from line_profiler import LineProfiler
        import random
        
        def do_other_stuff(numbers):
            s = sum(numbers)
        
        def do_stuff(numbers):
            do_other_stuff(numbers)
            l = [numbers[i]/43 for i in range(len(numbers))]
            m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        
        numbers = [random.randint(1,100) for i in range(1000)]
        lp = LineProfiler()
        lp.add_function(do_other_stuff)   # add additional function to profile
        lp_wrapper = lp(do_stuff)
        lp_wrapper(numbers)
        lp.print_stats()
        

        输出:

        Timer unit: 1e-06 s
        
        Total time: 9e-06 s
        File: <ipython-input-4-dae73707787c>
        Function: do_other_stuff at line 4
        
        Line #      Hits         Time  Per Hit   % Time  Line Contents
        ==============================================================
             4                                           def do_other_stuff(numbers):
             5         1            9      9.0    100.0      s = sum(numbers)
        
        Total time: 0.000694 s
        File: <ipython-input-4-dae73707787c>
        Function: do_stuff at line 7
        
        Line #      Hits         Time  Per Hit   % Time  Line Contents
        ==============================================================
             7                                           def do_stuff(numbers):
             8         1           12     12.0      1.7      do_other_stuff(numbers)
             9         1          208    208.0     30.0      l = [numbers[i]/43 for i in range(len(numbers))]
            10         1          474    474.0     68.3      m = ['hello'+str(numbers[i]) for i in range(len(numbers))]
        

        注意:以这种方式将函数添加到配置文件不需要更改配置文件的代码(即,无需添加 @profile 装饰器)。

        【讨论】:

        • 好答案!但我实际上发现装饰器是控制分析的好方法。有没有办法将 line_prof 与脚本内的装饰器一起使用(没有 kernprof)?
        • 我不是 100% 这行得通。我还没有测试过。感谢 tdube 提供 line_profiler 代码。我所做的只是转换一个现有的装饰器,我希望它可以与 LineProfiler 一起使用。编辑:格式化搞砸了,因为我不知道如何在 cmets 中格式化代码。 Here's a Pastebin.
        • 这就是我目前在我的 Django 代码中使用的(我不能使用命令行kernprof):stackoverflow.com/a/68163807/1937033
        猜你喜欢
        • 1970-01-01
        • 2023-03-22
        • 2017-09-02
        • 2019-01-24
        • 2018-07-26
        • 1970-01-01
        • 2018-02-06
        • 2015-05-07
        • 1970-01-01
        相关资源
        最近更新 更多