选自pythonfiles
机器之心编译
参与:Panda
前段时间,Python Files 博客发布了几篇主题为「Hunting Performance in Python Code」的系列文章,对提升 Python 代码的性能的方法进行了介绍。在其中的每一篇文章中,作者都会介绍几种可用于 Python 代码的工具和分析器,以及它们可以如何帮助你更好地在前端(Python 脚本)和/或后端(Python 解释器)中找到瓶颈。机器之心对这个系列文章进行了整理编辑,将其融合成了这一篇深度长文。本文的相关代码都已经发布在 GitHub 上。
代码地址:github.com/apatrascu/h…
第一部分请查看从环境设置到内存分析。以下是 Python 代码优化的第二部分,主要从 Python 脚本与 Python 解释器两个方面阐述。在这一部分中我们首先会关注如何追踪 Python 脚本的 CPU 使用情况,并重点讨论 cProfile、line_profiler、pprofile 和 vprof。而后一部分重点介绍了一些可用于在运行 Python 脚本时对解释器进行性能分析的工具和方法,主要讨论了 CPython 和 PyPy 等。
CPU 分析——Python 脚本
在这一节,我将介绍一些有助于我们解决 Python 中分析 CPU 使用的难题的工具。
CPU 性能分析(profiling)的意思是通过分析 CPU 执行代码的方式来分析这些代码的性能。也就是说要找到我们代码中的热点(hot spot),然后看我们可以怎么处理它们。
接下来我们会看看你可以如何追踪你的 Python 脚本的 CPU 使用。我们将关注以下分析器(profiler):
测量 CPU 使用
这一节我将使用与前一节基本一样的脚本,你也可以在 GitHub 上查看:gist.github.com/apatrascu/8…
另外,记住在 PyPy2 上,你需要使用一个支持它的 pip 版本:
其它东西可以通过以下指令安装:
cProfile
在 CPU 性能分析上最常用的一个工具是 cProfile,主要是因为它内置于 CPython2 和 PyPy2 中。这是一个确定性的分析器,也就是说它会在运行我们的负载时收集一系列统计数据,比如代码各个部分的执行次数或执行时间。此外,相比于其它内置的分析器(profile 或 hotshot),cProfile 对系统的开销更少。
当使用 CPython2 时,其使用方法是相当简单的:
如果你使用的是 PyPy2:
其输出如下:
即使是这样的文本输出,我们也可以直接看到我们脚本的大多数时间都在调用 list.append 方法。
如果我们使用 gprof2dot,我们可以用图形化的方式来查看 cProfile 的输出。要使用这个工具,我们首先必须安装 graphviz。在 Ubuntu 上,可以使用以下命令:
再次运行我们的脚本:
然后我们会得到下面的 output.png 文件:
这样看起来就轻松多了。让我们仔细看看它输出了什么。你可以看到来自脚本的函数调用图(callgraph)。在每个方框中,你可以一行一行地看到:
比如说,在从上到下第三个红色框中,方法 primes 占用了 98.28% 的时间,65.44% 的时间是在该方法之中做什么事情,它被调用了 40 次。剩下的时间被用在了 Python 的 list.append(22.33%)和 range(11.51%)方法中。
这是一个简单的脚本,所以我们只需要重写我们的脚本,让它不用使用那么多的 append 方法,结果如下:
以下测试了脚本在使用前和使用 CPython2 后的运行时间:
用 PyPy2 测量:
我们在 CPython2 上得到了 2.4 倍的提升,在 PyPy2 上得到了 3.1 倍的提升。很不错,其 cProfile 调用图为:
你也可以以程序的方式查看 cProfile:
这在一些场景中很有用,比如多进程性能测量。更多详情请参阅:docs.python.org/2/library/p…
line_profiler
这个分析器可以提供逐行水平的负载信息。这是通过 C 语言用 Cython 实现的,与 cProfile 相比计算开销更少。
其源代码可在 GitHub 上获取:github.com/rkern/line_…,PyPI 页面为:pypi.python.org/pypi/line_p…。和 cProfile 相比,它有相当大的开销,需要多 12 倍的时间才能得到一个分析结果。
要使用这个工具,你首先需要通过 pip 添加:pip install pip install Cython ipython==5.4.1 line_profiler(CPython2)。这个分析器的一个主要缺点是不支持 PyPy。
就像在使用 memory_profiler 时一样,你需要在你想分析的函数上加上一个装饰。在我们的例子中,你需要在 03.primes-v1.py 中的 primes 函数的定义前加上 @profile。然后像这样调用:
你会得到一个这样的输出:
我们可以看到两个循环在反复调用 list.append,占用了脚本的大部分时间。
pprofile
地址:github.com/vpelletier/…
来源: https://juejin.im/post/5a1539696fb9a045076f4f1a