"profiling" --- Python 性能分析器
*********************************

Added in version 3.15.

**源代码:** Lib/profiling/

======================================================================


性能分析简介
============

*性能分析结果 <profile>* 是一组统计数据，用于描述程序的各个部分执行的
频率和耗时。这些统计数据有助于识别性能瓶颈并指导优化工作。Python 提供
了两种从根本上不同的方法来收集这些信息：统计采样和确定性跟踪。

"profiling" 包将 Python 的内置性能分析工具组织在同一个命名空间下。它包
含两个子模块，每个子模块分别实现一种不同的性能分析方法：

"profiling.sampling"
   一种统计性能分析器，会定期对调用栈进行采样。可以直接运行脚本，也可
   以通过 PID 附加到正在运行的进程。它提供多种输出格式（火焰图、热图、
   Firefox Profiler）、GIL 分析、GC 跟踪以及多种性能分析模式（挂钟时间
   、CPU、GIL），并且几乎没有开销。

"profiling.tracing"
   一种确定性性能分析器，会跟踪每一次函数调用、返回和异常事件。它提供
   精确的调用次数和准确的计时信息，能够捕获每一次调用，包括执行速度很
   快的函数。

备注:

  性能分析器模块的设计目标是为给定程序提供执行性能分析结果，而不是用于
  基准测试。进行基准测试时，请使用 "timeit" 模块，它能提供相当准确的计
  时测量。在比较 Python 代码和 C 代码时，这一区别尤其重要：确定性性能
  分析器会给 Python 代码引入开销，但不会给 C 层级函数引入开销，这可能
  导致比较结果出现偏差。


选择性能分析器
==============

对于大多数性能分析任务，请使用统计性能分析器 ("profiling.sampling")。
它开销极小，既适用于开发环境也适用于生产环境，并提供丰富的可视化选项，
包括火焰图、热图、GIL 分析等。

当你需要 **精确的调用次数** 且不能遗漏任何函数调用时，请使用确定性性能
分析器 ("profiling.tracing")。由于它会检测每一次函数调用和返回，因此即
使是在采样间隔之间完成的极快函数也会被捕获。代价是开销更高。

下表总结了主要区别：

+----------------------+--------------------------------+--------------------------------+
| 特性                 | 统计采样                       | 确定性 ("profiling.tracing")   |
|                      | ("profiling.sampling")         |                                |
|======================|================================|================================|
| **开销**             | 几乎没有                       | 中等                           |
+----------------------+--------------------------------+--------------------------------+
| **准确性**           | 统计估计                       | 精确调用次数                   |
+----------------------+--------------------------------+--------------------------------+
| **输出格式**         | pstats、火焰图、热图、gecko、  | pstats                         |
|                      | 折叠格式                       |                                |
+----------------------+--------------------------------+--------------------------------+
| **性能分析模式**     | 挂钟时间、CPU、GIL             | 挂钟时间                       |
+----------------------+--------------------------------+--------------------------------+
| **特殊帧**           | GC、原生帧（C 扩展）           | N/A                            |
+----------------------+--------------------------------+--------------------------------+
| **附加到 PID**       | 是                             | 否                             |
+----------------------+--------------------------------+--------------------------------+


何时使用统计采样
----------------

对于大多数性能分析任务，建议使用统计性能分析器 ("profiling.sampling")
。它的使用方式与 "profiling.tracing" 相同:

   python -m profiling.sampling run script.py

采样性能分析器的主要优势之一是它支持多种输出格式。除了传统的 pstats 表
格之外，它还可以生成用于可视化调用层级的交互式火焰图、精确显示代码中时
间消耗位置的行级源代码热图，以及用于基于时间线分析的 Firefox Profiler
输出。

该性能分析器还可以洞察确定性性能分析无法捕获的 Python 解释器行为。使用
"--mode gil" 可识别多线程代码中的 GIL 争用，使用 "--mode cpu" 可测量排
除 I/O 等待后的实际 CPU 时间，或检查 "<GC>" 帧以了解垃圾回收开销。"--
native" 选项会显示 C 扩展中花费的时间，有助于区分 Python 开销和库性能
。

对于多线程应用，"-a" 选项会同时采样所有线程，以显示工作是如何分布的。
对于生产环境调试，"attach" 命令可以通过 PID 连接到任何正在运行的
Python 进程，而无需重启或修改代码。


何时使用确定性跟踪
------------------

确定性性能分析器 ("profiling.tracing") 会检测每一次函数调用和返回。此
方法的开销高于采样，但可以保证完整覆盖程序执行过程。

选择确定性跟踪的主要原因是你需要精确的调用次数。统计性能分析会基于采样
估计频率，可能会少计在两次采样之间完成的短生命周期函数。如果你需要验证
某项优化是否确实减少了函数调用次数，或者想跟踪完整调用图以理解调用方与
被调用方的关系，那么确定性跟踪就是合适的选择。

确定性跟踪也非常擅长捕获以微秒级执行的函数。这类函数在统计采样中可能不
会足够频繁地出现，但确定性跟踪会记录每一次调用，而不受持续时间影响。


快速入门
========

本节提供开始进行性能分析所需的最少步骤。完整文档请参阅各性能分析器的专
门页面。


统计性能分析
------------

要分析脚本的性能，请使用 "profiling.sampling" 模块并搭配 "run" 命令:

   python -m profiling.sampling run script.py
   python -m profiling.sampling run -m mypackage.module

这会在性能分析器下运行脚本，并打印一份显示时间消耗位置的摘要。要生成交
互式火焰图:

   python -m profiling.sampling run --flamegraph script.py

要分析一个已经在运行的进程，请使用 "attach" 命令并指定进程 ID:

   python -m profiling.sampling attach 1234

对于自定义设置，请指定采样间隔（以微秒为单位）和持续时间（以秒为单位）
:

   python -m profiling.sampling run -i 50 -d 30 script.py


确定性性能分析
--------------

要从命令行分析脚本的性能:

   python -m profiling.tracing script.py

要以编程方式分析一段代码的性能：

   import profiling.tracing
   profiling.tracing.run('my_function()')

这会在性能分析器下执行给定代码，并打印一份摘要，显示精确的函数调用次数
和计时。


理解性能分析输出
================

两种性能分析器都会收集函数级统计数据，但呈现格式不同。采样性能分析器提
供多种可视化形式（火焰图、热图、Firefox Profiler、pstats 表格），而确
定性性能分析器会生成兼容 pstats 的输出。无论格式如何，底层概念都是相同
的。

关键性能分析概念：

**直接时间** (也称为 *自身时间* 或 *tottime*)
   执行函数自身代码所花费的时间，不包括它调用的其他函数所花费的时间。
   直接时间较高表示该函数包含开销较大的操作。

**累计时间** (也称为 *总时间* 或 *cumtime*)
   在该函数及其调用的所有函数中花费的时间。这衡量的是调用一个函数的总
   成本，包括它的整个调用子树。

**调用次数** (也称为 *ncalls* 或 *samples*)
   函数被调用（确定性）或被采样（统计性）的次数。在确定性性能分析中，
   此值是精确的。在统计性能分析中，它表示该函数出现在栈样本中的次数。

**原始调用**
   不是由递归引起的调用。 当函数递归时，总调用次数包括递归调用，但原始
   调用次数只统计初始进入次数。 显示格式为 "total/primitive" (例如
   "3/1" 表示总共调用三次，其中一次为原始调用)。

**调用方/被调用方关系**
   哪些函数调用了给定函数（调用方），以及该函数调用了哪些函数（被调用
   方）。火焰图会将其可视化为嵌套矩形；pstats 可以通过
   "print_callers()" 和 "print_callees()" 方法显示这些关系。


旧版兼容性
==========

为了保持向后兼容，"cProfile" 模块仍可作为 "profiling.tracing" 的别名使
用。使用 "import cProfile" 的现有代码将在所有未来的 Python 版本中继续
无需修改即可运行。

自 3.15 版本弃用: 纯 Python 的 "profile" 模块已被弃用，并将在 Python
3.17 中移除。 请改用 "profiling.tracing" (或其别名 "cProfile")。 迁移
指导请参阅 "profile"。

参见:

  "profiling.sampling"
     带有火焰图、热图和 GIL 分析功能的统计采样性能分析器。推荐大多数用
     户使用。

  "profiling.tracing"
     用于精确调用次数的确定性跟踪性能分析器。

  "pstats"
     性能分析数据的统计分析和格式化。

  "timeit"
     用于测量小段代码执行时间的模块。

-[ 子模块 ]-

* "profiling.tracing" --- 确定型性能分析器

* "profiling.sampling" --- 统计型性能分析器
