使用 memory_profiler 在 Flask 应用程序中分析行

Posted

技术标签:

【中文标题】使用 memory_profiler 在 Flask 应用程序中分析行【英文标题】:Use memory_profiler to profile lines in a Flask app 【发布时间】:2018-01-10 17:57:59 【问题描述】:

更新:在 memory_profiler 0.53 及更高版本中,@profile 可以根据需要装饰任意数量的路线。早期版本只允许装饰一条路线。以下问题仅适用于版本 的 memory_profiler 版本

使用普通的 @profile 装饰器不适用于两个或多个 Flask 路由。如何在两个或多个 Flask 路由中逐行获取内存使用情况分析?

我想分析 /route_one 和 /route_two:

from functools import wraps

from memory_profiler import profile

@app.route("/route_one", methods=["GET"])
@profile
def route_one():
    api_live = ping_api()
    if not api_live:
        return make_response('', 503)
    return make_response('', 200)

@app.route("/route_two", methods=["GET"])
@profile
def route_two():
    api_live = ping_api()
    if not api_live:
        return make_response('', 503)
    return make_response('', 200)

当我使用上述装饰路线启动我的烧瓶应用程序时,我收到此错误:

AssertionError: View function mapping is overwriting an existing endpoint function: wrapper

【问题讨论】:

【参考方案1】:

memory_profiler 已更新以包含在 Flask 路由上使用 @profile 的代码。 memory_profiler version >= 0.53 不会有这个问题。请参阅this GitHub issue 了解更多信息。


错误告诉我们,在我们试图映射/装饰的两条路由上使用了相同的函数包装器。解决这个问题的方法是使用@wraps。如此处所述:What does functools.wraps do? @wraps 将名称和文档字符串从内部函数复制到外部包装函数。所以如果我们使用@wraps,就可以避免上面的错误。

但是我们需要在装饰器定义中使用@wraps。我们的配置文件装饰器是在 memory_profiler 库中定义的,因此我们需要重写该函数以包含 @wraps。 memory_profiler 分析器函数在这里https://github.com/pythonprofilers/memory_profiler/blob/master/memory_profiler.py,我们将在下面使用它的修改版本,它使用@wraps。

在您的烧瓶应用中使用以下代码 用@my_profiler 装饰你的路线

from functools import wraps

import memory_profiler
try:
    import tracemalloc
    has_tracemalloc = True
except ImportError:
    has_tracemalloc = False


def my_profiler(func=None, stream=None, precision=1, backend='psutil'):
    """
    Decorator that will run the function and print a line-by-line profile
    """
    backend = memory_profiler.choose_backend(backend)
    if backend == 'tracemalloc' and has_tracemalloc:
        if not tracemalloc.is_tracing():
            tracemalloc.start()
    if func is not None:
        @wraps(func)
        def wrapper(*args, **kwargs):
            prof = memory_profiler.LineProfiler(backend=backend)
            val = prof(func)(*args, **kwargs)
            memory_profiler.show_results(prof, stream=stream,
                                         precision=precision)
            return val

        return wrapper
    else:
        def inner_wrapper(f):
            return profile(f, stream=stream, precision=precision,
                           backend=backend)

        return inner_wrapper

我们现在可以使用我们的固定分析器了

@app.route("/route_one", methods=["GET"])
@my_profiler
def route_one():
    api_live = ping_api()
    if not api_live:
        return make_response('', 503)
    return make_response('', 200)

@app.route("/route_two", methods=["GET"])
@my_profiler
def route_two():
    api_live = ping_api()
    if not api_live:
        return make_response('', 503)
    return make_response('', 200)

【讨论】:

【参考方案2】:

profile 装饰器没有使用functools.wraps,因此它不保留函数名称。 app.route 在执行时将这两个函数都视为名为 'profile' 的函数。由于 Flask 不允许多个路由使用相同的名称,因此会引发错误。

在 memory_profiler 修复此问题之前,您可以通过在要应用 @profile 装饰器时临时指定端点名称来修复此问题。

@app.route('/one', endpoint='one')
@profile
@login_required
def one():
    return 'one'

@profile 应该在您想要包含在分析中的任何装饰器之上,@route 应该在 @profile 之上,以便它路由到视图的分析版本。

【讨论】:

以上是关于使用 memory_profiler 在 Flask 应用程序中分析行的主要内容,如果未能解决你的问题,请参考以下文章

使用memory_profiler时无法导入模块

如何使用 Python 多处理和 memory_profiler 分析多个子进程?

Python3 memory_profiler,同时使用lambda表达式连接PyQt5中的插槽

python3.6安装memory_profiler

psutil 是因为该包能提升 memory_profiler 的性能

无法将其他flas动画片段访问到动作脚本3