在Python 2.7和Python 3.6中,我都认为这可行:
from cProfile import Profile; p = Profile(); p.enable()
...这引发了异常:
from profile import Profile; p = Profile(); p.enable()
# --> AttributeError: 'Profile' object has no attribute 'enable'
这让我感到惊讶,因为我认为(以及Python 3和Python 2的官方文档)这两个模块应该提供相同的程序员接口:
profile
和cProfile
模块均提供以下功能:...
class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)
...
enable()
开始收集配置文件数据。
我想念什么? 使用profile.Profile()
实例的正确方法是什么?
答案 0 :(得分:1)
在 Python 3.8 和更新的文档中,他们已修复此问题以指定 enable()
和 disable()
仅适用于 cProfile
。对于 profile.Profile
,您需要使用 run()
调用,而不是 enable()
和 disable()
。以下是每个示例:
cProfile.Profile:
# cProfile.Profile -- control with enable() and disable()
from datetime import datetime
import cProfile, pstats, io
sortby = 'cumulative'
pr1 = cProfile.Profile(lambda: int(datetime.now().timestamp()*1000000), 0.000001)
pr1.enable()
list(x for x in range(int(10*10*10*10/10*10+10)))
pr1.disable()
s1 = io.StringIO()
ps1 = pstats.Stats(pr1, stream=s1).sort_stats(sortby)
ps1.print_stats()
print(s1.getvalue())
profile.profile:
# profile.Profile -- control with run()
from datetime import datetime
import profile, pstats, io
sortby = 'cumulative'
pr2 = profile.Profile(datetime.now().timestamp, 0.000001)
pr2.run('list(x for x in range(int(10*10*10*10/10*10+10)))')
s2 = io.StringIO()
ps2 = pstats.Stats(pr2, stream=s2).sort_stats(sortby)
ps2.print_stats()
print(s2.getvalue())
cProfile
会将分析函数的输出推送到 stdout,而 profile
不会。
最终报告的 filename:lineno(function)
列的输出略有不同。 cProfile
通常会更易读地显示内置函数。
profile
的输出中是可识别的,而在 cProfile
的输出中 不是 -- 但如果使用一个命名函数,它在两个分析器的输出中都是可识别的(尽管传递给函数的参数只能从profile
的输出中看到)。< /li>
通过 cProfile
和 profile
运行相同的函数会提供略有不同的结果。 cProfile
似乎通常会减少大约两个函数调用,并且将为同一函数提供更有效的时间。
使用分析器的可选自定义 timer
和 timeunit
参数可以提供获得 微秒 精度与默认毫秒相比的优势精确。然而,尝试使用这些似乎有一些警告:
cProfile
的 timeunit 函数需要一个整数,而 profile
需要一个浮点数。这意味着您不能直接将 datetime.now().timestamp()
与 cProfile
一起使用,而必须先将其包装在 lambda 中以将其转换为完整整数。profile
似乎无法与 datetime.now().timestamp()
一起正常工作,即使它是它所期望的格式。它将打印出负时间值——我在下面展示了这一点。切换到 lambda 形式以将其转换为整数无济于事,因为 profile
需要浮点数。以下是 profile.Profile
的输出,带有 datetime.now().timestamp()
计时器用于微秒供应。
print(s2.getvalue())
10015 function calls in -0.020 seconds`
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 :0(setprofile)
10011 -0.010 -0.000 -0.010 -0.000 <string>:1(<genexpr>)
1 -0.010 -0.010 -0.020 -0.020 <string>:1(<module>)
1 -0.000 -0.000 -0.020 -0.020 :0(exec)
1 -0.000 -0.000 -0.020 -0.020 profile:0(list(x for x in range(int(10*10*10*10/10*10+10))))
然而,这个问题可以通过提供一个自定义的计时器函数来解决(它甚至可以环绕 datetime.now()
以提供与 datetime.now().timestamp()
完全相同的输出格式),因为我不能'不解释。
例如如果我们定义:
def timenow():
now = datetime.now()
return (((now.year-1970) * 31557600) + (now.month * 2629800) +
(now.day * 86400) + (now.hour * 3600) + (now.minute * 60) +
now.second + (now.microsecond * 0.000001))
我们现在可以使用 pr2 = profile.Profile(timenow, 0.000001)
,这将输出以下(正常的)报告:
>>> print(s2.getvalue())
10015 function calls in 0.041 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.006 0.006 0.041 0.041 profile:0(list(x for x in range(int(10*10*10*10/10*10+10))))
1 0.000 0.000 0.035 0.035 :0(exec)
1 0.017 0.017 0.035 0.035 <string>:1(<module>)
10011 0.018 0.000 0.018 0.000 <string>:1(<genexpr>)
1 0.001 0.001 0.001 0.001 :0(setprofile)
0 0.000 0.000 profile:0(profiler)
与 cProfile
的输出(使用上面定义的自定义 lambda 计时器)相比,我们有:
>>> print(s1.getvalue())
10013 function calls in 0.031 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
10011 0.031 0.000 0.031 0.000 <stdin>:1(<genexpr>)
1 0.000 0.000 0.000 0.000 <stdin>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
至于为什么我们的自定义计时器函数给出了合理的结果而 datetime.now().timestamp()
对 profile
没有,首先必须注意的是 timenow
没有为 datetime.now().timestamp()
提供计时.尽管它提供了接近纪元的时间,但它的偏差很小(不会影响正常的配置文件运行,因为增量在同一系统上是相同的)。 timenow
中使用的手动转换也肯定不如 datetime.now().timestamp()
中的高效。我只能推测后者效率太高,结果 profile
源代码无法正确测量。
除非有特殊原因,否则请使用 cProfile
。官方 Python 文档还指出推荐的模块是 cProfile
。虽然没有提供具体的理由,但文档似乎暗示 cProfile
可能表现得更好,适合大多数用户的目的(尽管它的语法稍微冗长一些)。