# `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.