奇怪的是,您看到的是预期行为。在 Python 文档的 introduction to the profilers 部分中,它指出与 cProfile 相比,profile 增加了“分析程序的显着开销”。您看到的区别在于您正在使用的库,而不是您如何调用它们。考虑这个脚本:
import profile
import cProfile
def nothing():
return
def main():
for i in xrange(1000):
for j in xrange(1000):
nothing()
return
cProfile.run('main()')
profile.run('main()')
cProfile 的输出显示 main 运行大约需要 0.143 秒,而 profile 变体报告的运行时间为 1.645 秒,大约是 11.5 倍。
现在让我们再次将脚本更改为:
def nothing():
return
def main():
for i in xrange(1000):
for j in xrange(1000):
nothing()
return
if __name__ == "__main__":
main()
并使用分析器调用它:
python -m 配置文件 test_script.py
报告 main 运行 1.662 秒。
python -m cProfile test_script.py
报告 main 运行 0.143 秒。
这表明您启动分析器的方式与您在cProfile 和profile 之间看到的差异无关。不同之处在于两个分析器如何处理“事件”,例如函数调用或返回。在这两种情况下,执行代码中都有软件挂钩,它们会触发回调以跟踪这些事件并执行诸如更新事件计数器和启动或停止计时器之类的操作。然而,profile 模块在 Python 中本地处理所有这些事件,这意味着您的解释器必须离开您的代码,执行回调内容,然后返回以继续您的代码。
cProfile 也必须发生同样的事情(执行分析回调),但它更快,因为回调是用 C 编写的。查看两个模块文件 profile.py 和cProfile.py 展示了一些差异:
-
profile.py 是 610 行,而 cProfile.py 只有 199 行 - 它的大部分功能都在 C 中处理。
-
profile.py 主要使用 Python 库,而 cProfile.py 导入“_lsprof”,一个 C 代码文件。源码可以查看here。
-
profile.py 中的
Profile 类不继承自任何其他类(第 111 行),而 cProfile.py 中的Profile 类(第 66 行)继承自 _lsprof.Profiler,在 C 源文件中实现。
正如文档所述,cProfile 通常是要走的路,因为它主要是用 C 实现的,所以一切都更快。
顺便说一句,您可以通过校准来提高profile 的性能。有关如何做到这一点的详细信息available in the docs 还有更多有关如何/为什么所有这些东西的详细信息,请参见Deterministic Profiling 和 limitations 的 Python 文档部分。
TL;DR
cProfile 更快,因为正如它的名字所暗示的,它的大部分是用 C 实现的。这与 profile 模块形成对比,它必须处理本机 Python 中的所有分析回调。无论您是从命令行调用分析器还是在脚本中手动调用分析器,都不会影响两个模块之间的时间差。