项目Euler和其他编码竞赛通常有最长的运行时间或人们吹嘘他们的特定解决方案运行的速度。使用python,有时候这些方法有点笨拙 - 即将时间码添加到__main__
。
分析python程序运行多长时间的好方法是什么?
答案 0 :(得分:1207)
Python包含一个名为cProfile的探查器。它不仅给出了总运行时间,还给出了每个函数的单独时间,并告诉你每个函数被调用了多少次,这样就可以很容易地确定你应该在哪里进行优化。
您可以在代码中或从解释器中调用它,如下所示:
import cProfile
cProfile.run('foo()')
更有用的是,您可以在运行脚本时调用cProfile:
python -m cProfile myscript.py
为了使它更容易,我制作了一个名为'profile.bat'的小批处理文件:
python -m cProfile %1
所以我要做的就是运行:
profile euler048.py
我明白了:
1007 function calls in 0.061 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.061 0.061 <string>:1(<module>)
1000 0.051 0.000 0.051 0.000 euler048.py:2(<lambda>)
1 0.005 0.005 0.061 0.061 euler048.py:2(<module>)
1 0.000 0.000 0.061 0.061 {execfile}
1 0.002 0.002 0.053 0.053 {map}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.003 0.003 0.003 0.003 {sum}
编辑:更新了来自PyCon 2013的好视频资源的链接
Python Profiling
Also via YouTube
答案 1 :(得分:383)
不久之前,我制作了pycallgraph
,它可以从Python代码生成可视化。 修改:我已更新该示例以使用3.3,这是撰写本文时的最新版本。
在pip install pycallgraph
并安装GraphViz之后,您可以从命令行运行它:
pycallgraph graphviz -- ./mypythonscript.py
或者,您可以分析代码的特定部分:
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
with PyCallGraph(output=GraphvizOutput()):
code_to_profile()
其中任何一个都会生成类似于下图的pycallgraph.png
文件:
答案 2 :(得分:188)
值得指出的是,使用探查器仅在主线程上工作(默认情况下),如果您使用它们,则不会从其他线程获取任何信息。这可能有些问题,因为在profiler documentation中完全没有提及。
如果您还想对线索进行概要分析,则需要查看文档中的threading.setprofile()
function。
您还可以创建自己的threading.Thread
子类来执行此操作:
class ProfiledThread(threading.Thread):
# Overrides threading.Thread.run()
def run(self):
profiler = cProfile.Profile()
try:
return profiler.runcall(threading.Thread.run, self)
finally:
profiler.dump_stats('myprofile-%d.profile' % (self.ident,))
并使用ProfiledThread
类而不是标准类。它可能会给你更多的灵活性,但我不确定它是否值得,特别是如果你使用的第三方代码不会使用你的类。
答案 3 :(得分:135)
python wiki是一个分析资源的好页面: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code
和python文档一样: http://docs.python.org/library/profile.html
如Chris Lawlor所示cProfile是一个很棒的工具,可以很容易地用于打印到屏幕上:
python -m cProfile -s time mine.py <args>
或提交:
python -m cProfile -o output.file mine.py <args>
PS&GT;如果您使用的是Ubuntu,请确保安装python-profile
sudo apt-get install python-profiler
如果输出到文件,您可以使用以下工具获得良好的可视化效果
PyCallGraph:创建调用图图像的工具
安装:
sudo pip install pycallgraph
运行:
pycallgraph mine.py args
视图:
gimp pycallgraph.png
您可以使用您喜欢的任何内容来查看png文件,我使用了gimp
不幸的是我常常得到
dot:图表对于cairo-renderer位图来说太大了。按比例缩放0.257079以适合
这使我的图像非常小。所以我通常创建svg文件:
pycallgraph -f svg -o pycallgraph.svg mine.py <args>
PS&GT;确保安装graphviz(提供点程序):
sudo pip install graphviz
通过@maxy / @quodlibetor使用gprof2dot替代图形:
sudo pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
答案 4 :(得分:126)
@ Maxy对this answer的评论帮助了我,我认为它应该得到自己的答案:我已经有了cProfile生成的.pstats文件,我不想用pycallgraph重新运行,所以我使用了gprof2dot,得到了漂亮的svgs:
$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg
和BLAM!
它使用点(与pycallgraph使用的相同),因此输出看起来相似。我得到的印象是gprof2dot丢失的信息较少:
答案 5 :(得分:57)
在研究这个主题时,我遇到了一个名为SnakeViz的方便工具。 SnakeViz是一个基于Web的分析可视化工具。它非常易于安装和使用。我使用它的常用方法是使用%prun
生成一个stat文件,然后在SnakeViz中进行分析。
使用的主要技术是 Sunburst chart ,如下所示,其中函数调用的层次结构被安排为以角度宽度编码的弧和时间信息层。
最好的是你可以与图表互动。例如,要放大一个,可以单击一个圆弧,圆弧及其后代将被放大为新的旭日形状以显示更多细节。
答案 6 :(得分:43)
我认为cProfile
非常适合分析,而kcachegrind
非常适合可视化结果。中间的pyprof2calltree
处理文件转换。
python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree
安装所需的工具(至少在Ubuntu上):
apt-get install kcachegrind
pip install pyprof2calltree
结果:
答案 7 :(得分:39)
另外值得一提的是GUI cProfile转储查看器RunSnakeRun。它允许您进行排序和选择,从而放大程序的相关部分。图中矩形的大小与所花费的时间成比例。如果将鼠标悬停在矩形上,则会突出显示该表中的调用以及地图上的任何位置。双击矩形时,它会放大该部分。它将显示谁调用该部分以及该部分调用的内容。
描述性信息非常有用。它显示了该位的代码,在处理内置库调用时可能会有所帮助。它告诉你找到代码的文件和行。
还想指出OP表示&#39; profiling&#39;但似乎他的意思是&#39;时间&#39;请记住,在分析时程序运行速度会变慢。
答案 8 :(得分:31)
一个很好的分析模块是line_profiler(使用脚本kernprof.py调用)。可以下载here。
我的理解是cProfile仅提供有关每个函数花费的总时间的信息。因此,各行代码都没有定时。这是科学计算中的一个问题,因为通常一条线可能需要花费很多时间。另外,正如我记得的那样,cProfile没有抓住我在numpy.dot上花费的时间。
答案 9 :(得分:29)
最简单和最快找到所有时间的方式。
1. pip install snakeviz
2. python -m cProfile -o temp.dat <PROGRAM>.py
3. snakeviz temp.dat
在浏览器中绘制饼图。最大的一块是问题功能。非常简单。
答案 10 :(得分:29)
line_profiler
(已在此处提供)也启发了pprofile
,其描述如下:
线粒度,线程感知确定性和统计纯python 分析器
它提供行级粒度为line_profiler
,是纯Python,可以用作独立命令或模块,甚至可以生成可以使用[k|q]cachegrind
轻松分析的callgrind格式文件。
还有vprof,一个Python包描述为:
为各种Python程序特性(如运行时间和内存使用情况)提供丰富的交互式可视化。
答案 11 :(得分:15)
我最近创建了tuna来可视化Python运行时和导入配置文件;这可能会有所帮助。
安装
pip3 install tuna
创建运行时配置文件
python -mcProfile -o program.prof yourfile.py
或导入配置文件(需要Python 3.7 +)
python -X importprofile yourfile.py 2> import.log
然后在文件上运行金枪鱼
tuna program.prof
答案 12 :(得分:12)
根据Joe Shaw关于多线程代码不能按预期工作的回答,我认为cProfile中的runcall
方法只是围绕profiled函数调用进行self.enable()
和self.disable()
调用,所以你可以自己做,只需要你想要的任何代码,对现有代码的干扰最小。
答案 13 :(得分:12)
有许多很棒的答案,但他们要么使用命令行,要么使用一些外部程序进行分析和/或对结果进行排序。
我真的错过了一些我可以在我的IDE(eclipse-PyDev)中使用的方法,而无需触及命令行或安装任何东西。所以就是这样。
def count():
from math import sqrt
for x in range(10**5):
sqrt(x)
if __name__ == '__main__':
import cProfile, pstats
cProfile.run("count()", "{}.profile".format(__file__))
s = pstats.Stats("{}.profile".format(__file__))
s.strip_dirs()
s.sort_stats("time").print_stats(10)
有关详细信息,请参阅docs或其他答案。
答案 14 :(得分:10)
在Virtaal的source中,有一个非常有用的类和装饰器,可以很容易地进行分析(甚至是特定的方法/函数)。然后可以在KCacheGrind中非常舒服地查看输出。
答案 15 :(得分:9)
cProfile非常适合快速分析,但大部分时间它都以错误结束。函数runctx通过正确初始化环境和变量来解决这个问题,希望它对某人有用:
import cProfile
cProfile.runctx('foo()', None, locals())
答案 16 :(得分:6)
我的方法是使用yappi(https://code.google.com/p/yappi/)。它与RPC服务器结合起来特别有用,其中(甚至仅用于调试)您注册方法来启动,停止和打印分析信息,例如,这样:
@staticmethod
def startProfiler():
yappi.start()
@staticmethod
def stopProfiler():
yappi.stop()
@staticmethod
def printProfiler():
stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
statPrint = '\n'
namesArr = [len(str(stat[0])) for stat in stats.func_stats]
log.debug("namesArr %s", str(namesArr))
maxNameLen = max(namesArr)
log.debug("maxNameLen: %s", maxNameLen)
for stat in stats.func_stats:
nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
log.debug('nameAppendSpaces: %s', nameAppendSpaces)
blankSpace = ''
for space in nameAppendSpaces:
blankSpace += space
log.debug("adding spaces: %s", len(nameAppendSpaces))
statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"
log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
log.log(1000, statPrint)
然后,当您的程序工作时,您可以随时通过调用startProfiler
RPC方法启动探查器并通过调用printProfiler
将分析信息转储到日志文件(或修改rpc方法以将其返回到调用者)并得到这样的输出:
2014-02-19 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000:
name ncall ttot tsub
2014-02-19 16:32:24,128-|SVR-MAIN |-(Thread-3 )-Level 1000:
C:\Python27\lib\sched.py.run:80 22 0.11 0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293 22 0.11 0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515 22 0.11 0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66 1 0.0 0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243 4 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4 0.0 0.0
<string>.__new__:8 220 0.0 0.0
C:\Python27\lib\socket.py.close:276 4 0.0 0.0
C:\Python27\lib\threading.py.__init__:558 1 0.0 0.0
<string>.__new__:8 4 0.0 0.0
C:\Python27\lib\threading.py.notify:372 1 0.0 0.0
C:\Python27\lib\rfc822.py.getheader:285 4 0.0 0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301 1 0.0 0.0
C:\Python27\lib\xmlrpclib.py.end:816 3 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467 1 0.0 0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460 1 0.0 0.0
C:\Python27\lib\SocketServer.py.close_request:475 1 0.0 0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066 4 0.0 0.0
对于短脚本可能不是很有用,但有助于优化服务器类型的过程,特别是考虑到printProfiler
方法可以多次调用以分析和比较,例如不同的程序使用场景。
答案 17 :(得分:6)
仅适用于终端的(也是最简单的)解决方案,以防所有这些精美的UI无法安装或运行:
完全忽略cProfile
并将其替换为pyinstrument
,它将在执行后立即收集并显示调用树。
安装:
$ pip install pyinstrument
配置文件和显示结果:
$ python -m pyinstrument ./prog.py
适用于python2和3。
答案 18 :(得分:6)
如果要制作累积探查器, 意思是连续运行该函数几次并观察结果的总和。
您可以使用以下cumulative_profiler
装饰器:
特定于python> = 3.6,但是您可以删除nonlocal
,因为它可以在旧版本上使用。
import cProfile, pstats
class _ProfileFunc:
def __init__(self, func, sort_stats_by):
self.func = func
self.profile_runs = []
self.sort_stats_by = sort_stats_by
def __call__(self, *args, **kwargs):
pr = cProfile.Profile()
pr.enable() # this is the profiling section
retval = self.func(*args, **kwargs)
pr.disable()
self.profile_runs.append(pr)
ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
return retval, ps
def cumulative_profiler(amount_of_times, sort_stats_by='time'):
def real_decorator(function):
def wrapper(*args, **kwargs):
nonlocal function, amount_of_times, sort_stats_by # for python 2.x remove this row
profiled_func = _ProfileFunc(function, sort_stats_by)
for i in range(amount_of_times):
retval, ps = profiled_func(*args, **kwargs)
ps.print_stats()
return retval # returns the results of the function
return wrapper
if callable(amount_of_times): # incase you don't want to specify the amount of times
func = amount_of_times # amount_of_times is the function in here
amount_of_times = 5 # the default amount
return real_decorator(func)
return real_decorator
示例
分析功能baz
import time
@cumulative_profiler
def baz():
time.sleep(1)
time.sleep(2)
return 1
baz()
baz
运行了5次并打印了此内容:
20 function calls in 15.003 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
10 15.003 1.500 15.003 1.500 {built-in method time.sleep}
5 0.000 0.000 15.003 3.001 <ipython-input-9-c89afe010372>:3(baz)
5 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
指定次数
@cumulative_profiler(3)
def baz():
...
答案 19 :(得分:5)
用于在IPython笔记本上快速获取配置文件统计信息。 可以将 line_profiler 和 memory_profiler 直接嵌入他们的笔记本中。
另一个有用的软件包是 Pympler 。它是一个功能强大的配置文件包,能够跟踪类,对象,函数,内存泄漏等。下面的示例,附带的文档。
!pip install line_profiler
!pip install memory_profiler
!pip install pympler
%load_ext line_profiler
%load_ext memory_profiler
%time print('Outputs CPU time,Wall Clock time')
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs
礼物:
%timeit -r 7 -n 1000 print('Outputs execution time of the snippet')
#1000 loops, best of 7: 7.46 ns per loop
%prun -s cumulative 'Code to profile'
礼物:
%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB
礼物:
#Example function
def fun():
for i in range(10):
print(i)
#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()
礼物:
sys.getsizeof('code to profile')
# 64 bytes
以字节为单位返回对象的大小。
from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))
pympler.asizeof可用于调查某些Python对象消耗多少内存。 与sys.getsizeof相比,asizeof递归地调整对象大小
from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
li = [1,2,3]
di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()
跟踪功能的生命周期。
Pympler软件包包含大量用于概要分析代码的实用工具。所有这些都不能在这里涵盖。请参阅随附的文档,以获取详细的配置文件实现。
答案 20 :(得分:3)
在Python中处理分析的新工具是PyVmMonitor:http://www.pyvmmonitor.com/
它有一些独特的功能,例如
注意:它是商业广告,但免费提供开源。
答案 21 :(得分:3)
添加到https://stackoverflow.com/a/582337/1070617,
我编写了这个模块,允许您使用cProfile并轻松查看其输出。更多信息:https://github.com/ymichael/cprofilev
$ python -m cprofilev /your/python/program
# Go to http://localhost:4000 to view collected statistics.
另请参阅:http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html了解如何理解收集的统计数据。
答案 22 :(得分:3)
这取决于您想要从分析中看到什么。简单的时间 度量可以由(bash)给出。
time python python_prog.py
偶数&#39; / usr / bin / time&#39;可以使用&#39; - 详细&#39;输出详细的指标标志。
要检查每个函数给出的时间度量并更好地了解函数花费的时间,可以在python中使用内置的cProfile。
进入更详细的指标,如性能,时间不是唯一的指标。您可以担心内存,线程等
分析选项:
1. line_profiler 是另一种常用于逐行查找时序指标的分析器。
2. memory_profiler 是一个分析内存使用情况的工具
3. heapy(来自项目Guppy)描述如何使用堆中的对象。
这些是我倾向于使用的一些常见的。但是如果你想了解更多信息,请尝试阅读book 在开始考虑性能方面,这是一本非常好的书。您可以转到使用Cython和JIT(即时)编译的python的高级主题。
答案 23 :(得分:3)
想知道python脚本到底在做什么吗?输入 检查壳牌。 Inspect Shell允许您打印/更改全局变量并运行 函数不会中断正在运行的脚本。现在用 自动完成和命令历史记录(仅限Linux)。
Inspect Shell不是pdb样式的调试器。
https://github.com/amoffat/Inspect-Shell
你可以使用它(以及你的手表)。
答案 24 :(得分:2)
使用austin之类的统计分析器,不需要任何检测,这意味着您可以轻松地从Python应用程序中分析数据
austin python3 my_script.py
原始输出不是很有用,但是您可以将其通过管道传输到flamegraph.pl 以获得该数据的火焰图表示,从而可以了解花费时间(以毫秒为单位的实时时间)的细分情况。
austin python3 my_script.py | flamegraph.pl > my_script_profile.svg
答案 25 :(得分:2)
最近我为 PyCharm 创建了一个插件,您可以使用它在 PyCharm 编辑器中轻松分析和可视化 line_profiler
的结果。
line_profiler
在其他答案中也提到过,它是一个很好的工具,可以准确分析 Python 解释器在某些行中花费了多少时间。
我创建的 PyCharm 插件可以在这里找到: https://plugins.jetbrains.com/plugin/16536-line-profiler
它需要在你的 python 环境中一个名为 line-profiler-pycharm
的帮助包,它可以通过 pip 或插件本身安装。
在 PyCharm 中安装插件后:
line_profiler_pycharm.profile
装饰器装饰您想要分析的任何函数答案 26 :(得分:1)
还有一个名为statprof
的统计分析器。它是一个采样分析器,因此它为您的代码增加了最小的开销,并提供了基于行的(而不仅仅是基于功能的)时序。它更适合像游戏这样的软实时应用程序,但可能没有cProfile那么精确。
version in pypi有点陈旧,因此可以通过指定the git repository与pip
一起安装:
pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01
您可以像这样运行:
import statprof
with statprof.profile():
my_questionable_function()
答案 27 :(得分:1)
我刚从pypref_time开发了自己的探查器:
https://github.com/modaresimr/auto_profiler
通过添加装饰器,它将显示一棵耗时的函数树
@Profiler(depth=4, on_disable=show)
Install by: pip install auto_profiler
import time # line number 1
import random
from auto_profiler import Profiler, Tree
def f1():
mysleep(.6+random.random())
def mysleep(t):
time.sleep(t)
def fact(i):
f1()
if(i==1):
return 1
return i*fact(i-1)
def show(p):
print('Time [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
'-----------------------------------------------------------------------')
print(Tree(p.root, threshold=0.5))
@Profiler(depth=4, on_disable=show)
def main():
for i in range(5):
f1()
fact(3)
if __name__ == '__main__':
main()
Time [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974] main [auto-profiler/profiler.py:267] [/test/t2.py:30]
├── 5.954s [5 * 1.191] f1 [/test/t2.py:34] [/test/t2.py:14]
│ └── 5.954s [5 * 1.191] mysleep [/test/t2.py:15] [/test/t2.py:17]
│ └── 5.954s [5 * 1.191] <time.sleep>
|
|
| # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020] fact [/test/t2.py:36] [/test/t2.py:20]
├── 0.849s [1 * 0.849] f1 [/test/t2.py:21] [/test/t2.py:14]
│ └── 0.849s [1 * 0.849] mysleep [/test/t2.py:15] [/test/t2.py:17]
│ └── 0.849s [1 * 0.849] <time.sleep>
└── 2.171s [1 * 2.171] fact [/test/t2.py:24] [/test/t2.py:20]
├── 1.552s [1 * 1.552] f1 [/test/t2.py:21] [/test/t2.py:14]
│ └── 1.552s [1 * 1.552] mysleep [/test/t2.py:15] [/test/t2.py:17]
└── 0.619s [1 * 0.619] fact [/test/t2.py:24] [/test/t2.py:20]
└── 0.619s [1 * 0.619] f1 [/test/t2.py:21] [/test/t2.py:14]
答案 28 :(得分:0)
当我不是服务器上的root用户时,我会使用 lsprofcalltree.py并运行我的程序:
python lsprofcalltree.py -o callgrind.1 test.py
然后我可以使用任何与callgrind兼容的软件打开报告,例如qcachegrind
答案 29 :(得分:0)
gprof2dot_magic
gprof2dot
的魔术函数可将任何Python语句配置为JupyterLab或Jupyter Notebook中的DOT图。
GitHub存储库:https://github.com/mattijn/gprof2dot_magic
安装
确保您拥有Python软件包gprof2dot_magic
。
pip install gprof2dot_magic
其依赖项gprof2dot
和graphviz
也将被安装
用法
要启用魔术功能,请先加载gprof2dot_magic
模块
%load_ext gprof2dot_magic
然后将任何行语句配置为DOT图,如下所示:
%gprof2dot print('hello world')