使用 line_profiler 进行 Python 分析 - 即时删除 @profile 语句的巧妙方法?

Posted

技术标签:

【中文标题】使用 line_profiler 进行 Python 分析 - 即时删除 @profile 语句的巧妙方法?【英文标题】:Python profiling using line_profiler - clever way to remove @profile statements on-the-fly? 【发布时间】:2013-08-16 06:45:11 【问题描述】:

我想使用出色的line_profiler,但只是在某些时候。为了使它工作,我添加了

@profile

在每个函数调用之前,例如

@profile
def myFunc(args):
    blah
    return

并执行

kernprof.py -l -v mycode.py args

但我不想每次都手动放入 @profile 装饰器,因为大多数时候我想在没有它们的情况下执行代码,如果我尝试包含它们会出现异常,例如

mycode.py args

是否有一种快乐的媒介,我可以根据某些条件开关/参数动态删除装饰器,而无需手动执行操作和/或过多地修改每个函数?

【问题讨论】:

我会问自己,我是否真的需要如此频繁地进行分析,以至于它需要这样的支持。我不是说你做或不做,所以你不需要回答。我只是发现这个用例有点令人惊讶。 代码需要很长时间(目前是几个小时......)才能执行,所以现在我想用一块石头杀死两只鸟,同时获得结果和分析。我想我认为分析是一个持续的过程(因为我是新手/对此感到兴奋),所以我不会只在(许多)函数中使用它,声明它完成并删除所有装饰器。 如果没有trying this,我不会让事情花费数小时。它不花费任何费用,并且可以准确地告诉您正在发生的事情。 @jtlz2 您还可以包装函数和类方法以进行分析(可能在单独的分析脚本中)并避免完全按照此处所述添加@profile 装饰器(***.com/a/43376466/5874320)。 【参考方案1】:

不要删除 @profile 装饰器行,而是提供您自己的传递无操作版本。

您可以将以下代码添加到您的项目中的某处:

try:
    # Python 2
    import __builtin__ as builtins
except ImportError:
    # Python 3
    import builtins

try:
    builtins.profile
except AttributeError:
    # No line profiler, provide a pass-through version
    def profile(func): return func
    builtins.profile = profile

在使用 @profile 装饰器的任何代码之前导入此代码,您可以在行分析器处于活动状态或不处于活动状态的情况下使用代码。

由于虚拟装饰器是一个传递函数,因此不会影响执行性能(只有导入性能受到非常轻微的影响)。

如果你不喜欢弄乱内置插件,你可以把它做成一个单独的模块;说profile_support.py

try:
    # Python 2
    import __builtin__ as builtins
except ImportError:
    # Python 3
    import builtins

try:
    profile = builtins.profile
except AttributeError:
    # No line profiler, provide a pass-through version
    def profile(func): return func

(没有赋值builtins.profile)并在任何使用@profile装饰器的模块中使用from profile_support import profile

【讨论】:

这个问题是,如果你想用kernprof -l --setup script.py script.py之类的东西“启动”你的运行,它会用noop覆盖@profile装饰器。 @JonnyRobbie:然后使用另一个装饰器来确定 builtins.profile 在脚本运行时是否存在,而不是在导入时。 --setup 发生的情况是代码导入时 builtins.profile 未设置,所有代码都用 no-op 装饰器装饰,因此在实际分析运行时没有任何分析。【参考方案2】:

您根本不需要导入__builtins__/builtinsLineProfiler,您可以在尝试查找profile 时简单地依赖NameError

try:
    profile
except NameError:
    profile = lambda x: x

但是,这需要包含在每个使用 profile 的文件中,但它不会(永久)改变 Python 的全局状态(内置)。

【讨论】:

【参考方案3】:

逐渐成为@Martijin Pieters 答案变体的评论。

我不想让__builtin__ 参与进来。如果没有评论,其他人几乎不可能猜到 line_profiler 参与其中,没有先验知识。

查看kernprofline 199,实例化LineProfiler就足够了。

try:
    from line_profiler import LineProfiler
    profile = LineProfiler()
except ImportError:
    def profile(func):
        return func

导入(显式)is better 而不是全局修改 builtins(隐式)。如果分析装饰器是永久性的,那么它们的来源应该在代码本身中是明确的。

在存在line_profiler 的情况下,上述方法将在每次运行时使用分析器包装修饰函数,无论是否由kernprof 运行。这种副作用可能是不受欢迎的。

【讨论】:

【参考方案4】:

我正在使用 Python 3.4 的以下修改版本

try:
    import builtins
    profile = builtins.__dict__['profile']
except KeyError:
    # No line profiler, provide a pass-through version
    def profile(func): return func

【讨论】:

【参考方案5】:

其他答案是正确的,但在尝试使用 kernprof -l --setup script.py script.py 之类的“启动”脚本时可能会出现问题。像这样启动脚本可能很有用,例如当您尝试使用 numba 优化您的函数并且您不想通过编译(或从文件系统缓存加载,即使使用 numbas cache=True 参数)。

问题是安装程序运行noops 你所有的@profile 装饰器并使它们对分析运行没有意义。

我通过将 try except 移动到实际的装饰器运行来解决这个问题:

def profile2(f):
    def s(*args, **kwargs):
        try:
            return profile(f)(*args, **kwargs)
        except NameError:
            return f(*args, **kwargs)
    return s

并用@profile2 装饰我的所有配置文件。

【讨论】:

以上是关于使用 line_profiler 进行 Python 分析 - 即时删除 @profile 语句的巧妙方法?的主要内容,如果未能解决你的问题,请参考以下文章

Python,django:用line_profiler工具分析代码的性能

line_profiler 不返回任何输出

Python 分析:使用 line_profiler 的 @profile 装饰器会导致错误

Python Line_profiler 和 Cython 函数

Python line_profiler 找不到模块

python line_profiler,speedup,timeit