术→技巧, 研发

Python执行时间的可视化

钱魏Way · · 1 次浏览

在前面的文章Python标准库之性能优化中学习到了统计Python代码执行时间的工具。今天要学习的是一些其他的工具。

SnakeViz

SnakeViz简介

SnakeViz 是一个用于可视化 Python 代码性能分析结果的工具。它是基于 Python 的 cProfile 模块生成的分析数据进行可视化展示的,提供了一种更直观的方式来理解代码的性能瓶颈和执行路径。

主要功能

  • 可视化性能数据:SnakeViz 将 cProfile 生成的分析数据转化为图形化的表示,使得开发者可以通过图形界面更容易地理解程序的性能表现。
  • Sunburst 图:使用 Sunburst 图来展示函数调用的层次结构和各个函数的耗时比例。外圈代表更深层次的函数调用,内圈则是外层调用。
  • 支持大数据集:SnakeViz 可以处理大型分析数据集,并且提供交互式的界面来帮助用户浏览和分析数据。
  • 交互式界面:提供了交互式的 Web 界面,用户可以通过点击图形来查看详细的性能数据。

应用场景

  • 性能优化:开发者可以通过 SnakeViz 找出程序中的性能瓶颈,优化关键路径,提高程序运行效率。
  • 教育用途:对于学习性能分析的学生或开发者,SnakeViz 提供了一种简单易懂的方式来理解程序执行过程。
  • 代码审查:在进行代码审查时,使用 SnakeViz 可以帮助团队更好地理解代码的性能特征,从而做出更明智的优化决策。

注意事项

  • 性能开销:使用 cProfile 和 SnakeViz 进行性能分析会对程序本身的执行时间产生一定的影响,因此建议在开发或测试环境中使用。
  • 适用范围:适用于需要详细分析函数调用关系和时间消耗的 Python 项目,尤其是那些性能关键的应用。

SnakeViz使用示例

创建一个名为 complex_example.py 的文件,并在其中添加以下代码:

import time
import math

def compute_square_roots(n):
    result = []
    for i in range(n):
        result.append(math.sqrt(i))
    return result

def simulate_heavy_computation(n):
    for _ in range(n):
        time.sleep(0.01)

def calculate_factorials(n):
    result = []
    for i in range(1, n):
        result.append(math.factorial(i))
    return result

def main():
    start_time = time.time()
    
    # Compute square roots of first 10000 numbers
    compute_square_roots(10000)
    
    # Simulate some heavy computation
    simulate_heavy_computation(100)
    
    # Calculate factorials of first 100 numbers
    calculate_factorials(100)
    
    end_time = time.time()
    print(f"Total computation time: {end_time - start_time:.2f} seconds")

if __name__ == "__main__":
    main()

安装SnakeViz

SnakeViz 可以通过 pip 进行安装:pip install snakeviz

生成性能分析数据

使用 cProfile 生成分析文件,在终端或命令提示符中运行以下命令:

python -m cProfile -o complex_output.prof complex_example.py

这将运行 complex_example.py 并生成一个名为 complex_output.prof 的性能分析文件。

使用 SnakeViz 可视化分析数据

在终端或命令提示符中运行以下命令来可视化分析数据:

snakeviz complex_output.prof

这将启动一个本地的 Web 服务器,并在默认浏览器中打开一个新标签页,展示分析结果。

分析结果

  • Sunburst 图:在浏览器中,你将看到一个 Sunburst 图,展示 compute_square_roots、simulate_heavy_computation 和 calculate_factorials 函数的相对执行时间。
  • 交互功能:通过点击图中的不同部分,你可以查看每个函数的详细性能数据,比如调用次数、总耗时和平均耗时。

解释分析结果

  • compute_square_roots和 calculate_factorials 函数主要耗费 CPU 时间,因为它们涉及大量计算。
  • simulate_heavy_computation函数由于包含 sleep(0.01),其主要耗时来自于等待时间。

通过 SnakeViz 的可视化图形,开发者可以识别出哪些函数是性能瓶颈,从而帮助优化程序的整体性能。这个示例展示了如何使用 SnakeViz 来分析一个包含多种计算任务的 Python 脚本。

gprof2dot

gprof2dot简介

gprof2dot 是一个用于将程序性能分析工具生成的输出结果转化为图形化调用图(call graph)的 Python 脚本。它可以帮助开发者更直观地理解程序的性能瓶颈和函数调用关系。

主要功能

  • 支持多种分析工具:gprof2dot支持多种性能分析工具的输出,包括 gprof、oprofile、perf、Python 的 cProfile 和 pstats 等。
  • 生成调用图:将分析结果转换为调用图,展示函数之间的调用关系和各个函数的性能指标(如耗时、调用次数等)。
  • 图形化展示:生成的调用图可以通过 Graphviz 工具进行可视化,提供清晰的图形界面来分析性能数据。
  • 自定义样式:支持自定义节点和边的样式,以突出显示特定的性能指标。

应用场景

  • 性能分析:帮助开发者识别程序中的性能瓶颈,优化函数调用路径。
  • 代码审查:在代码审查过程中,通过调用图更好地理解代码的执行流程。
  • 教学和学习:用于教学和学习,帮助学生理解复杂程序的执行逻辑。

注意事项

  • 依赖性:生成图形需要安装 Graphviz 工具。
  • 输入格式:需要确保分析工具生成的输出格式与gprof2dot 支持的格式匹配。
  • 大规模项目:对于非常大的项目,生成的调用图可能会很复杂,难以阅读。在这种情况下,可以考虑过滤或聚合部分数据。

gprof2dot使用示例

安装gprof2dot

gprof2dot 可以通过 pip 安装:pip install gprof2dot

安装 Graphviz

确保你的系统上安装了 Graphviz,以便能够生成图形化的调用图。可以通过包管理器安装(例如,brew install graphviz 在 macOS 上,sudo apt-get install graphviz 在 Ubuntu 上)。Windows安装Graphviz的可执行包,并在环境变量的PATH添加安装路径。

使用 cProfile 生成分析数据

我们将使用 Python 的 cProfile 模块来生成性能分析数据,保存为 output.prof 文件:

python -m cProfile -o complex_output.prof complex_example.py

使用 gprof2dot 可视化分析数据

使用 gprof2dot 将 cProfile 生成的分析数据转换为 dot 格式:

gprof2dot -f pstats complex_output.prof -o complex_output.dot

这里,-f pstats 指定输入文件格式为 pstats,-o complex_output.dot 指定输出的 dot 文件名。

生成调用图

使用 Graphviz 将 dot 文件转换为图形格式(如 PNG):

dot -Tpng complex_output.dot -o complex_output.png

这将生成一个名为 complex_output.png 的图像文件,你可以使用任何图像查看器打开它。

分析结果

  • 调用图:生成的调用图将展示 slow_function 和 fast_function 的调用关系和性能数据。
  • 性能指标:每个节点(函数)会显示调用次数、总耗时等信息,帮助你识别性能瓶颈。

line_profiler

line_profiler简介

line_profiler 是一个用于逐行分析 Python 代码性能的工具。它通过提供每一行代码的执行时间信息,帮助开发者识别性能瓶颈并进行优化。与 Python 自带的 cProfile 不同,line_profiler 更加细粒度地提供了代码行级别的性能数据。

主要功能

  • 逐行性能分析:line_profiler可以报告每一行代码的执行时间,使开发者能够精确定位性能瓶颈。
  • 装饰器使用:使用装饰器标记需要分析的函数,简单直观。
  • 多次调用分析:可以分析同一行代码在多次函数调用中的平均耗时。
  • 集成工具:与 Jupyter Notebook 集成良好,可以直接在 Notebook 中使用。

应用场景

  • 性能优化:通过识别代码中耗时的行,帮助开发者优化关键代码段。
  • 代码审查:在代码审查过程中,提供关于代码性能的细粒度数据。
  • 教学和学习:帮助学习者理解代码执行的性能特征。

注意事项

  • 性能开销:由于逐行分析的开销较大,建议在开发或测试环境中使用,而非生产环境。
  • 装饰器限制:需要在代码中手动添加装饰器来标记需要分析的函数。
  • 内存消耗:对于非常大的代码库或长时间运行的程序,生成的分析数据可能会比较大。

line_profiler使用示例

安装line_profiler:

line_profiler 可以通过 pip 安装:pip install line_profiler

标记函数:

在你的 Python 脚本中,使用 @profile 装饰器来标记需要进行性能分析的函数。例如:

@profile
def example_function():
    total = 0
    for i in range(1000):
        total += i
    return total

example_function()

line_profiler 在进行性能分析时,确实不需要在代码中显式地导入任何模块。这是因为 line_profiler 主要通过命令行工具 kernprof 来运行脚本,并对指定的函数进行分析。

如果使用的是 Jupyter Notebook,可能需要使用 %load_ext line_profiler 来加载扩展。

运行分析

通过 kernprof 命令来运行你的脚本,并生成性能分析数据:

kernprof -l -v example_script.py

其中,-l 选项表示使用 line_profiler,-v 选项表示显示分析结果。

查看结果

Wrote profile results to example_script.py.lprof
Timer unit: 1e-06 s

Total time: 0.0004769 s
File: example_script.py
Function: example_function at line 1

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           @profile
     2                                           def example_function():
     3         1          0.5      0.5      0.1      total = 0
     4      1001        219.6      0.2     46.0      for i in range(1000):
     5      1000        256.0      0.3     53.7          total += i
     6         1          0.8      0.8      0.2      return total

分析完成后,line_profiler 会输出每个被标记函数的逐行性能数据,包括:

  • 行号
  • 执行次数
  • 每行代码的总耗时
  • 每行代码的平均耗时

Pyinstrument

Pyinstrument简介

Pyinstrument 是一个用于分析 Python 程序性能的轻量级分析器(profiler)。它以简单易用、输出直观而著称,特别适合快速识别和理解代码的性能瓶颈。与传统的性能分析工具相比,Pyinstrument 更加关注可读性和用户体验。

主要功能

  • 低开销:Pyinstrument 设计为对程序的性能影响最小,确保分析过程中不会显著影响程序的运行时间。
  • 直观的输出:生成的报告以树状结构展示,清晰地显示函数调用关系和每个部分的耗时。
  • 简单的命令行接口:通过命令行工具快速分析 Python 脚本,无需修改代码。
  • 支持多种输出格式:可以生成 HTML、文本和 JSON 格式的报告,便于不同场景下查看和分享。
  • 支持 Jupyter Notebook:可以直接在 Jupyter Notebook 中使用,方便数据科学家和研究人员进行性能分析。

应用场景

  • 性能调试:快速识别代码中的性能瓶颈,优化代码执行效率。
  • 代码审查:在代码审查过程中,提供关于代码性能的直观数据。
  • 学习和教学:帮助学习者理解代码的执行时间分布。

注意事项

  • 分析精度:Pyinstrument 更适合用于宏观的性能分析,而非逐行分析。对于需要逐行分析的场景,可能需要结合其他工具(如line_profiler)。
  • I/O 操作:在进行大量 I/O 操作的程序中,Pyinstrument 可能无法准确反映 CPU 密集型任务的耗时。

Pyinstrument使用示例

安装

Pyinstrument 可以通过 pip 安装:pip install pyinstrument

命令行工具

使用 Pyinstrument 的命令行工具来分析 Python 脚本:

pyinstrument example_script.py

运行上述命令后,Pyinstrument 会执行 my_script.py 并在终端中显示性能分析报告。

Total computation time: 1.05 seconds

  _     ._   __/__   _ _  _  _ _/_   Recorded: 15:10:03  Samples:  108
 /_//_/// /_\ / //_// / //_'/ //     Duration: 1.056     CPU time: 0.000
/   _/                      v5.0.0

Program: .\complex_example.py

1.051 <module>  complex_example.py:1
└─ 1.051 main  complex_example.py:24
   └─ 1.047 simulate_heavy_computation  complex_example.py:12
      └─ 1.047 sleep  <built-in>

To view this report with different options, run:
    pyinstrument --load-prev 2024-11-17T15-10-03 [options]

作为上下文管理器

可以在代码中使用 Pyinstrument 作为上下文管理器,以编程方式获取性能报告:

from pyinstrument import Profiler

profiler = Profiler()
profiler.start()

# 需要分析的代码
def example_function():
    total = 0
    for i in range(1000):
        total += i
    return total

example_function()

profiler.stop()

print(profiler.output_text(unicode=True, color=True))

在 Jupyter Notebook 中使用

在 Jupyter Notebook 中,可以使用魔法命令来分析代码块:

%load_ext pyinstrument

# 使用魔法命令
%%pyinstrument
def example_function():
    total = 0
    for i in range(1000):
        total += i
    return total

example_function()

输出示例

Pyinstrument 的输出通常包括以下信息:

  • 总耗时:程序执行的总时间。
  • 函数调用树:显示函数的调用关系和每个函数的耗时。
  • 每个函数的耗时百分比:帮助快速识别性能瓶颈。

Linaro MAP for Python

Linaro MAP 是 Arm Forge 套件的一部分,是一种高性能计算(HPC)应用的性能分析工具。虽然 Linaro MAP 本身不是专门为 Python 设计的,但它可以用于分析使用 Python 编写的科学计算应用程序,尤其是在混合使用 C/C++、Fortran 和 Python 的复杂 HPC 应用中。以下是 Linaro MAP 的一些关键特性及其在 Python 开发中的应用。

Linaro MAP 的关键特性

  • 多语言支持:支持 C、C++、Fortran 以及 Python 等多种编程语言,适用于混合语言环境的性能分析。
  • 低开销:设计为对程序的性能影响最小,确保在分析过程中不会显著影响应用的执行时间。
  • 详细的性能分析:提供函数级别和源代码级别的性能数据,包括 CPU 使用率、I/O 活动、内存带宽等。
  • 并行性能分析:专注于并行应用程序的性能分析,支持 MPI 和 OpenMP 等并行编程模型。
  • 直观的可视化:提供图形化用户界面,帮助开发者直观地理解应用程序的性能特征和瓶颈。

在 Python 开发中的应用

虽然 Linaro MAP 更常用于 C/C++ 和 Fortran 等语言的 HPC 应用,Python 开发者也可以在以下场景中受益:

  • 混合语言应用:在使用 Python 调用 C/C++ 扩展(如使用 Cython 或 ctypes)时,可以利用 Linaro MAP 分析整体应用的性能。
  • 科学计算:对于使用 NumPy、SciPy 等库进行科学计算的 Python 应用,Linaro MAP 可以帮助识别计算密集型任务的性能瓶颈。
  • 并行计算:在使用 Python 的并行计算库(如 mpi4py)时,Linaro MAP 可以分析和优化并行性能。

使用 Linaro MAP 分析 Python 应用

  • 准备环境:确保 Linaro MAP 已正确安装,并且 Python 环境配置完毕。
  • 运行应用程序:使用 Linaro MAP 的命令行工具或图形界面启动 Python 应用程序的性能分析。
  • 分析报告:生成的报告将展示应用程序在不同函数和代码段上的性能数据,帮助识别和优化瓶颈。
  • 优化代码:根据分析结果,优化关键代码段,调整并行策略或改进算法以提高性能。

优势和局限性

  • 优势:
    • 适用于复杂的混合语言和并行应用。
    • 提供详尽的性能数据和直观的可视化界面。
  • 局限性:
    • 对于单纯的 Python 应用,可能不如专门针对 Python 的分析工具(如 Pyinstrument 或 cProfile)方便。
    • 主要面向 HPC 和并行计算领域,普通的 Python 开发者可能不常用。

参考链接:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注