The Python Profilers

The Python Profilers

源代码: Lib / profile.py和Lib / pstats.py

1.介绍分析器

cProfileprofile提供Python程序的确定性分析。一个配置文件是一组统计数据,描述如何经常和执行的程序多久各个部分。这些统计数据可以通过pstats模块格式化成报告。

Python标准库提供了相同概要分析界面的三种不同实现:

  • cProfile建议大多数用户使用; 这是一个C扩展,具有合理的开销,使其适合分析长时间运行的程序。基于lsprofBrett Rosen和Ted Czotter的贡献。2.5版本中的新功能。

2. profile,它是一个纯粹的Python模块,其接口被模仿cProfile,但是它为剖析程序增加了大量开销。如果您试图以某种方式扩展探查器,则使用此模块可能会更轻松。最初由Jim Roskind设计和撰写。

在版本2.4中进行了更改:现在还报告了调用内置函数和方法所花费的时间。

3. hotshot是一个实验性的C模块,其重点是最大限度地减少分析开销,代价是数据后处理时间更长。它不再被维护,并可能在未来的Python版本中被丢弃。 在版本2.5中进行了更改:结果应该比过去更有意义:时序核心包含一个严重错误。

  • 随着profile,数字显示在括号中每个调用者后,显示此特定调用了多少次提出。为方便起见,第二个非括号的数字重复在右侧的函数中花费的累积时间。

  • 同时cProfile,每个调用者前面都有三个数字:该特定调用的次数,以及该特定调用者调用当前函数所花费的总时间和累计时间。

print_callees(*restrictions)

这个Stats类的方法打印一个由指定函数调用的所有函数的列表。除了调用方向的这种逆转(re:称为vs被调用)之外,参数和顺序与print_callers()方法相同。

5.什么是确定性分析?

确定性分析旨在反映所有函数调用函数返回异常事件都受到监视,并且对这些事件之间的间隔(在此期间用户的代码正在执行)进行精确定时。相比之下,统计分析(这不是由该模块完成的)随机采样有效指令指针,并推断出在哪里花费时间。后一种技术传统上涉及较少的开销(因为代码不需要检测),但仅提供时间花费在何处的相对指示。

在Python中,由于在执行期间有一个解释器处于活动状态,因此不需要使用检测代码来执行确定性分析。Python会为每个事件自动提供一个钩子(可选回调)。此外,Python的解释性质往往会增加执行的开销,确定性分析往往只会在典型应用程序中增加小的处理开销。其结果是确定性分析并不昂贵,但它提供了有关Python程序执行的大量运行时统计信息。

通话计数统计可用于识别代码中的错误(令人惊讶的计数),并识别可能的内联扩展点(高通话计数)。内部时间统计可以用来识别应该仔细优化的“热循环”。应使用累积时间统计来识别算法选择中的高级错误。请注意,在此分析器中对累计时间的异常处理允许将算法的递归实现的统计信息直接与迭代实现进行比较。

6.限制

一个限制与时间信息的准确度有关。确定性分析器涉及准确性存在根本问题。最明显的限制是底层的“时钟”仅以约0.001秒的速率(典型值)滴答。因此,没有测量结果比底层时钟更准确。如果进行了足够的测量,那么“错误”将趋于平均。不幸的是,消除第一个错误会导致第二个错误。

第二个问题是,从事件发布到分析器调用获取时间的“调用需要一段时间”实际上会获得时钟状态。类似地,从获取时钟值(然后松鼠消失)的时间退出事件探查器时,存在一定的滞后,直到用户的代码再次执行。因此,多次调用或调用多个函数的函数通常会累积该错误。以这种方式积累的误差通常小于时钟的精度(小于一个时钟刻度),但它可以累积并变得非常重要。

profile与低开销相比,问题更重要cProfile。由于这个原因,profile为给定的平台提供了一种校准方法,以便可以将这个错误概率地(平均地)移除。在分析器校准后,它会更准确(以最小二乘的方式),但它有时会产生负数(当通话计数特别低时,概率神对你起作用:-)。)通过配置文件中的负数惊慌。它们应该在您校准过轮廓仪时出现,并且结果实际上比没有校准时好。

7.校准

profile模块的分析器从每个事件处理时间中减去一个常量,以补偿调用时间函数的开销,并解除结果。默认情况下,常量为0.可以使用以下过程为给定平台获取更好的常量(请参阅限制)。

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

该方法执行由参数给出的Python调用的次数,直接并且再次在分析器下测量两者的时间。然后计算每个事件探查器隐藏的开销,并将其作为一个浮点数返回。例如,在运行Mac OS X的1.8Ghz Intel Core i5上,使用Python的time.clock()作为定时器,神奇的数字约为4.04e-6。

这个练习的目的是得到一个相当一致的结果。如果您的计算机速度非常快,或者您的计时器功能的分辨率较差,则可能必须通过100000甚至1000000才能获得一致的结果。

当你有一个一致的答案时,有三种方法可以使用它:[1]

import profile # 1. Apply computed bias to all Profile instances created hereafter. profile.Profile.bias = your_computed_bias # 2. Apply computed bias to a specific Profile instance. pr = profile.Profile() pr.bias = your_computed_bias # 3. Specify computed bias in instance constructor. pr = profile.Profile(bias=your_computed_bias)

如果你有选择,你最好选择一个较小的常数,然后你的结果“不经常”在配置文件统计中显示为负值。

8.使用自定义计时器

如果您想更改当前时间的确定方式(例如,强制使用挂钟时间或流逝的处理时间),请将所需的计时函数传递给Profile类构造函数:

pr = profile.Profile(your_time_func)

然后生成的分析器将会调用your_time_func。根据您使用的profile.Profile还是cProfile.Profileyour_time_func返回值的解释不同:

profile.Profile

your_time_func应该返回一个单一的数字,或者是一个总数是当前时间的数字列表(比如os.times()返回的)。如果函数返回单个时间数字,或者返回的数字列表的长度为2,那么您将得到特别快速的派遣例程。

被警告您应该校准您选择的定时器功能的探查器类别(请参阅校准)。对于大多数机器来说,返回单个整数值的计时器将在分析过程中提供低成本方面的最佳结果。(os.times()非常糟糕的,因为它返回浮点值的元组)。如果您想以最简洁的方式替换更好的计时器,可以派生出一个班级并硬连线替代派遣方法,以最佳方式处理您的计时器呼叫以及适当的校准常数。

cProfile.Profile

your_time_func应该返回一个数字。如果它返回整数,也可以用第二个参数调用类构造函数,该参数指定一个单位时间的实际持续时间。例如,如果your_integer_time_func返回时间以千位秒为单位进行计算,则您将按Profile如下所示构造实例:

pr = cProfile.Profile(your_integer_time_func, 0.001)

由于cProfile.Profile班级无法校准,因此应小心使用自定义计时器功能,并且应尽可能快。为了使用定制定时器获得最佳结果,可能需要在内部_lsprof模块的C源代码中对其进行硬编码。

脚注

在Python 2.2之前,有必要编辑探查器源代码以将偏差作为文字编号嵌入。你仍然可以,但是这种方法不再被描述,因为不再需要。