"profile" --- Pure Python profiler
**********************************

**Source code:** Lib/profile.py

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

从 3.15 版起已弃用，将在 3.17 版中移除.

The "profile" module is deprecated and will be removed in Python 3.17.
Use "profiling.tracing" instead.

The "profile" module provides a pure Python implementation of a
deterministic profiler. While useful for understanding profiler
internals or extending profiler behavior through subclassing, its pure
Python implementation introduces significant overhead compared to the
C-based "profiling.tracing" module.

For most profiling tasks, use:

* "profiling.sampling" for production debugging with zero overhead

* "profiling.tracing" for development and testing


Migration
=========

Migrating from "profile" to "profiling.tracing" is straightforward.
The APIs are compatible:

   # Old (deprecated)
   import profile
   profile.run('my_function()')

   # New (recommended)
   import profiling.tracing
   profiling.tracing.run('my_function()')

For most code, replacing "import profile" with "import
profiling.tracing" (and using "profiling.tracing" instead of "profile"
throughout) provides a straightforward migration path.

备注:

  The "cProfile" module remains available as a backward-compatible
  alias to "profiling.tracing". Existing code using "import cProfile"
  will continue to work without modification.


"profile" and "profiling.tracing" module reference
==================================================

Both the "profile" and "profiling.tracing" modules provide the
following functions:

profile.run(command, filename=None, sort=-1)

   此函数接受一个可被传递给 "exec()" 函数的单独参数，以及一个可选的文
   件名。在所有情况下这个例程都会执行:

      exec(command, __main__.__dict__, __main__.__dict__)

   并收集执行过程中的性能分析统计数据。如果未提供文件名，则此函数会自
   动创建一个 "Stats" 实例并打印一个简单的性能分析报告。如果指定了
   sort 值，则它会被传递给这个 "Stats" 实例以控制结果的排序方式。

profile.runctx(command, globals, locals, filename=None, sort=-1)

   此函数类似于 "run()"，带有为 *command* 字符串提供 globals 和 locals
   映射对象的附加参数。这个例程会执行:

      exec(command, globals, locals)

   并像在上述的 "run()" 函数中一样收集性能分析数据。

class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)

   This class is normally only used if more precise control over
   profiling is needed than what the "profiling.tracing.run()"
   function provides.

   可以通过 *timer* 参数提供一个自定义计时器来测量代码运行花费了多长时
   间。它必须是一个返回代表当前时间的单个数字的函数。如果该数字为整数
   ，则 *timeunit* 指定一个表示每个时间单位持续时间的乘数。例如，如果
   定时器返回以千秒为计量单位的时间值，则时间单位将为 ".001"。

   直接使用 "Profile" 类将允许格式化性能分析结果而无需将性能分析数据写
   入到文件:

      import profiling.tracing
      import pstats
      import io
      from pstats import SortKey

      pr = profiling.tracing.Profile()
      pr.enable()
      # ... do something ...
      pr.disable()
      s = io.StringIO()
      sortby = SortKey.CUMULATIVE
      ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
      ps.print_stats()
      print(s.getvalue())

   The "Profile" class can also be used as a context manager
   (supported only in "profiling.tracing", not in the deprecated
   "profile" module; see 上下文管理器类型):

      import profiling.tracing

      with profiling.tracing.Profile() as pr:
          # ... do something ...

          pr.print_stats()

   在 3.8 版本发生变更: 添加了上下文管理器支持。

   enable()

      Start collecting profiling data. Only in "profiling.tracing".

   disable()

      Stop collecting profiling data. Only in "profiling.tracing".

   create_stats()

      停止收集分析数据，并在内部将结果记录为当前 profile。

   print_stats(sort=-1)

      根据当前性能分析数据创建一个 "Stats" 对象并将结果打印到 stdout。

      The *sort* parameter specifies the sorting order of the
      displayed statistics. It accepts a single key or a tuple of keys
      to enable multi-level sorting, as in
      "pstats.Stats.sort_stats()".

      Added in version 3.13: 现在 "print_stats()" 可接受一个由键组成的
      元组。

   dump_stats(filename)

      将当前 profile 的结果写入 *filename* 。

   run(cmd)

      通过 "exec()" 对该命令进行性能分析。

   runctx(cmd, globals, locals)

      通过 "exec()" 并附带指定的全局和局部环境对该命令进行性能分析。

   runcall(func, /, *args, **kwargs)

      对 "func(*args, **kwargs)" 进行性能分析

请注意性能分析只有在被调用的命令/函数确实能返回时才可用。如果解释器被
终结（例如在被调用的命令/函数执行期间通过 "sys.exit()" 调用）则将不会
打印性能分析结果。


Differences from "profiling.tracing"
====================================

The "profile" module differs from "profiling.tracing" in several ways:

**Higher overhead.** The pure Python implementation is significantly
slower than the C implementation, making it unsuitable for profiling
long-running programs or performance-sensitive code.

**Calibration support.** The "profile" module supports calibration to
compensate for profiling overhead. This is not needed in
"profiling.tracing" because the C implementation has negligible
overhead.

**Custom timers.** Both modules support custom timers, but "profile"
accepts timer functions that return tuples (like "os.times()"), while
"profiling.tracing" requires a function returning a single number.

**Subclassing.** The pure Python implementation is easier to subclass
and extend for custom profiling behavior.


What is deterministic profiling?
================================

*Deterministic profiling* is meant to reflect the fact that all
*function call*, *function return*, and *exception* events are
monitored, and precise timings are made for the intervals between
these events (during which time the user's code is executing).  In
contrast, *statistical profiling* (which is provided by the
"profiling.sampling" module) periodically samples the effective
instruction pointer, and deduces where time is being spent.  The
latter technique traditionally involves less overhead (as the code
does not need to be instrumented), but provides only relative
indications of where time is being spent.

在 Python 中，由于在执行过程中总有一个活动的解释器，因此执行确定性评测
不需要插入指令的代码。Python 自动为每个事件提供一个 *钩子* （可选回调
）。此外，Python 的解释特性往往会给执行增加太多开销，以至于在典型的应
用程序中，确定性分析往往只会增加很小的处理开销。结果是，确定性分析并没
有那么代价高昂，但是它提供了有关 Python 程序执行的大量运行时统计信息。

调用计数统计信息可用于识别代码中的错误（意外计数），并识别可能的内联扩
展点（高频调用）。内部时间统计可用于识别应仔细优化的 "热循环" 。累积时
间统计可用于识别算法选择上的高级别错误。请注意，该分析器中对累积时间的
异常处理，允许直接比较算法的递归实现与迭代实现的统计信息。


局限性
======

一个限制是关于时间信息的准确性。确定性性能分析存在一个涉及精度的基本问
题。最明显的限制是，底层的 “时钟” 周期大约为 0.001 秒（通常）。因此，
没有什么测量会比底层时钟更精确。如果进行了足够的测量，那么 “误差” 将趋
于平均。不幸的是，消除第一个误差会引入第二个误差来源。

第二个问题是，从调度事件到分析器调用获取时间函数实际 *获取* 时钟状态，
这需要 "一段时间" 。类似地，从获取时钟值（然后保存）开始，直到再次执行
用户代码为止，退出分析器事件句柄时也存在一定的延迟。因此，多次调用单个
函数或调用多个函数通常会累积此错误。尽管这种方式的误差通常小于时钟的精
度（小于一个时钟周期），但它 *可以* 累积并变得非常可观。

The problem is more important with the deprecated "profile" module
than with the lower-overhead "profiling.tracing".  For this reason,
"profile" provides a means of calibrating itself for a given platform
so that this error can be probabilistically (on the average) removed.
After the profiler is calibrated, it will be more accurate (in a least
square sense), but it will sometimes produce negative numbers (when
call counts are exceptionally low, and the gods of probability work
against you :-). )  Do *not* be alarmed by negative numbers in the
profile.  They should *only* appear if you have calibrated your
profiler, and the results are actually better than without
calibration.


校准
====

The profiler of the "profile" module subtracts a constant from each
event handling time to compensate for the overhead of calling the time
function, and socking away the results.  By default, the constant is
0. The following procedure can be used to obtain a better constant for
a given platform (see 局限性).

   import profile
   pr = profile.Profile()
   for i in range(5):
       print(pr.calibrate(10000))

此方法将执行由参数所给定次数的 Python 调用，在性能分析器之下直接和再次
地执行，并对两次执行计时。 它将随后计算每个性能分析器事件的隐藏开销，
并将其以浮点数的形式返回。例如，在一台运行 macOS 的 1.8Ghz Intel Core
i5 上，使用 Python 的 time.process_time() 作为计时器，魔数大约为
4.04e-6。

此操作的目标是获得一个相当稳定的结果。如果你的计算机 *非常* 快速，或者
你的计时器函数的分辨率很差，你可能必须传入 100000，甚至 1000000，才能
得到稳定的结果。

当你有一个一致的答案时，有三种方法可以使用:

   import profile

   # 1. 将计算出的偏差应用于此后创建的所有 Profile 实例。
   profile.Profile.bias = your_computed_bias

   # 2. 将计算出的偏差应用于特定的 Profile 实例。
   pr = profile.Profile()
   pr.bias = your_computed_bias

   # 3. 在实例构造函数中指定计算出的偏差。
   pr = profile.Profile(bias=your_computed_bias)

如果你可以选择，那么选择更小的常量会更好，这样你的结果将“更不容易”在性
能分析统计中显示负值。


使用自定义计时器
================

如果你想要改变当前时间的确定方式（例如，强制使用时钟时间或进程持续时间
），请向 "Profile" 类构造器传入你想要的计时函数:

   pr = profile.Profile(your_time_func)

The resulting profiler will then call "your_time_func". Depending on
whether you are using "profile.Profile" or
"profiling.tracing.Profile", "your_time_func"'s return value will be
interpreted differently:

"profile.Profile"
   "your_time_func" 应当返回一个数字，或一个总和为当前时间的数字列表（
   如同 "os.times()" 所返回的内容）。 如果该函数返回一个数字，或所返回
   的数字列表长度为 2，则你将得到一个特别快速的调度例程版本。

   请注意你应当为你选择的计时器函数校准性能分析器类 (参见 校准)。 对于
   大多数机器来说，一个返回长整数值的计时器在性能分析期间将提供在低开
   销方面的最佳结果。 ("os.times()" 是 *相当* 糟糕的，因为它返回一个浮
   点数值的元组)。 如果你想以最干净的方式替换一个更好的计时器，请派生
   一个类并硬连线一个能最佳地处理计时器调用的替换调度方法，并使用适当
   的校准常量。

"profiling.tracing.Profile"
   "your_time_func" 应当返回一个数字。如果它返回整数，你还可以通过第二
   个参数指定一个单位时间的实际持续长度来唤起类构造器。 举例来说，如果
   "your_integer_time_func" 返回以千秒为单位的时间，则你应当以如下方式
   构造 "Profile" 实例:

      pr = profiling.tracing.Profile(your_integer_time_func, 0.001)

   As the "profiling.tracing.Profile" class cannot be calibrated,
   custom timer functions should be used with care and should be as
   fast as possible. For the best results with a custom timer, it
   might be necessary to hard-code it in the C source of the internal
   "_lsprof" module.

Python 3.3 在 "time" 中添加了几个可被用来精确测量进程或时钟时间的新函
数。例如，参见 "time.perf_counter()".

参见:

  "profiling"
     Overview of Python profiling tools.

  "profiling.tracing"
     Recommended replacement for this module.

  "pstats"
     Statistical analysis and formatting for profile data.
