咱们能够经过查看程序核心算法的代码,得知核心算法的渐进上界或者下界,从而大概估计出程序在运行时的效率,可是这并不够直观,也不必定十分靠谱(在总体程序中仍有一些不可忽略的运行细节在估计时被忽略了),所以在实际评测程序时咱们仍是须要实际的考量程序的运行时间和瓶颈,最好具体到执行一段代码多少次,执行一段代码花了多少时间,幸亏的是Python自带了许多有用的工具,能够帮助咱们实现这些要求,下面是一些我在学习中记录的笔记,从简单到复杂介绍了python性能分析的方法,但愿个人笔记能帮到您。html
注:写做不易,转载请注明出处,谢谢支持~python
timeit能够在命令行经过-m指令导入做为脚本运行,也能够在代码内import导入使用,它会将代码执行多遍,而后得出耗时最短的时间是多少,下面是具体的几种使用方式:
注:我在实际测试时使用的是python3.7的环境,在文末,有举例说明python2和python3.7使用时实际上不一样的地方git
1.1 经过在命令行编译时添加-m
指令,来进行timeit的导入,后面跟着一段字符串,包含用来测试的表达式。(结果中的usec为微秒)
您也能够直接在字符串中包含你要测试的模块名,python会直接执行它,并用timeit得出时间:
这里在命令行的工做目录下,有一个test_timeit.py文件,其中的内容以下:算法
def factorial(n): if n == 1: return 1 return n * factorial(n-1) if __name__ == "__main__": factorial(30)
timeit计算了执行main的时间windows
1.2 经过添加 -n N
命令能够设置语句执行的次数:
ide
1.3 经过添加-r N
设置计时器重复多少次(默认是5次)(最后的结果是取平均?):
函数
1.4 经过添加-s str
设置str语句只在初始化的时候执行一遍,后面会pass这个语句:
图片过小,把命令写一下:
python -m timeit -n 100 -r 5 -s "from test_timeit import factorial" "factorial(20)"
其中-s
后跟着的"from test_timeit import factorial"
只在第一次时执行了
其中的factorial是一个计算阶乘的小函数,代码以下:
def factorial(n): if n == 1: return 1 return n * factorial(n-1)
工具
1.5 经过添加-t
使用time.time()
(default on Unix)oop
time.time()
返回从纪元(1970.1.1)至今的秒数。虽然这个函数的返回值永远为浮点数,
但并非全部系统提供的秒数都会精确到小数点之后。
通常状况下这个函数的返回值不会小于它上一次被调用的返回值,除非系统时钟在两次调用之间发生了重置。
//参考https://www.cnblogs.com/cuixiaochen/p/4722387.html性能
1.6 经过添加-c
使用 time.clock()
(default on Windows)
time.clock()
在Unix 中,将当前的处理器时间以浮点数的形式返回,单位为秒。
它的精确度(准确地说是“处理器时间”的精确度)取决于同名的C函数,
不管如何,这个函数是python关于时间计算的标尺。
WINDOWS中,第一次调用,返回的是进程运行的实际时间。
而第二次以后的调用是自第一次调用之后到如今的运行时间。
(其实是以WIN32上QueryPerformanceCounter()为基础,它比毫秒表示更为精确)
在windows中,time.clock()更准确。//参考https://www.cnblogs.com/cuixiaochen/p/4722387.html
在windows中使用-t
和-c
: 能够发现两者是有区别的,这个选项彻底能够遵循默认,timeit会自动使用更精确的时间计算方法
1.7 经过添加-v
,打印原始计时结果,以得到更高的数字精度,而且显示更具体的结果
1.8 经过-u
,设置计时单位:
可选项包括:nsec(纳秒),usec(微秒),msec(毫秒),sec(秒)
1.9 经过添加-p计算处理时间,而不是wallclock(从测试开始到结束所用的时间,以及 CPU 时间,即 CPU 上总的处理时间)
1.10 执行多行代码,只需在后面按顺序添加多个表达式便可:
1.11 经过添加-v
打印帮助:python -m timeit -h
,打印的内容以下:
C:\python37\mypython\learning test 2019-03>python -m timeit -h Tool for measuring execution time of small code snippets. This module avoids a number of common traps for measuring execution times. See also Tim Peters' introduction to the Algorithms chapter in the Python Cookbook, published by O'Reilly. Library usage: see the Timer class. Command line usage: python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) -r/--repeat N: how many times to repeat the timer (default 5) -s/--setup S: statement to be executed once initially (default 'pass'). Execution time of this setup statement is NOT timed. -p/--process: use time.process_time() (default is time.perf_counter()) -v/--verbose: print raw timing results; repeat for more digits precision -u/--unit: set the output time unit (nsec, usec, msec, or sec) -h/--help: print this usage message and exit --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a separate argument; indented lines are possible by enclosing an argument in quotes and using leading spaces. Multiple -s options are treated similarly. If -n is not given, a suitable number of loops is calculated by trying successive powers of 10 until the total time is at least 0.2 seconds. Note: there is a certain baseline overhead associated with executing a pass statement. It differs between versions. The code here doesn't try to hide it, but you should be aware of it. The baseline overhead can be measured by invoking the program without arguments. Classes: Timer Functions: timeit(string, string) -> float repeat(string, string) -> list default_timer() -> float
2.1 使用timeit.timeit
timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)
参数解释:
;
分开time.perf_counter()
使用实例:
Q&A: 为何不带number参数会比number=10**5时所费时间更长呢?
Q&A: 输出结果的默认单位是什么?
经过setup参数,设置初始执行的语句,下例是'x = 2',当重复执行多遍时,'x = 2'只在第一次执行一次。
经过globals携带全局变量
执行多个语句
2.2 使用timeit.repeat,输出的是个列表,包含重复次数个的测试时间
timeit.repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000, globals=None)
参数解释:
;
分开time.perf_counter()
使用起来与timeit.timeit相似,只是多了repeat参数,下例将timeit.repeat和timeit.timeit进行了对比,可见repeat只是重复了屡次而已
2.3 使用timeit.default_timer(),获取计时器,默认是 time.perf_counter(),示例:
备注:
两个计时器—— time.perf_counter()和time.process_time()
time.perf_counter()
返回性能计数器的值(以小数秒为单位),即具备最高分辨率的时钟测量一段很短的时间。它确实包括睡眠期间通过的时间,而且是全系统的.返回值的引用点未定义,所以只有连续调用的结果之间的差别是有效的。
time.process_time():
返回系统和当前进程的用户CPU时间之和的值(以小数秒为单位)。它不包括睡眠期间通过的时间。它的定义是全过程的。
返回值的引用点未定义,所以只有连续调用的结果之间的差别是有效的。
在一些背景下,“时间”有两种不一样的类型:绝对时间和相对时间。
绝对时间是“真实世界时间”,由time.time()咱们都习惯于处理这个问题。它一般是从过去的一个不动点(例如,01/01/1970 UTC的UNIX时代)以致少1秒的分辨率来测量的。现代系统一般提供毫秒或微秒分辨率。它由大多数计算机上的专用硬件维护,RTC(实时时钟)电路一般由电池供电,所以系统可以实时跟踪电源之间的变化。这个“真实世界时间”也会根据你的位置(时区)和季节(夏时制)进行修改,或者表示为与UTC(也被称为格林尼治标准时间或祖鲁时间)的抵消。
第二,有相对时间,由time.perf_counter和time.process_time。这种类型的时间与现实世界的时间没有明确的关系,从某种意义上说,这种关系是系统和实现特定的。它只能用于测量时间间隔,即与两个瞬间之间的时间成正比的单位数。这主要用于评估相对性能(例如,此版本的代码运行速度是否比代码的版本更快)。
在现代系统中,它是用CPU计数器测量的,它在与CPU硬件时钟相关的频率上单调增长。计数器的分辨率高度依赖于系统的硬件,在大多数状况下,它的值不能可靠地与现实世界的时间相关,甚至没法在系统间进行比较。此外,每次启动或重置CPU时,计数器值都会被重置。
time.perf_counter返回计数器的绝对值。time.process_time是一个值,该值从CPU计数器派生而来,但仅在给定进程在CPU上运行时才更新,能够细分为“用户时间”,即进程自己在CPU上运行的时间,以及“系统时间”,即操做系统内核表明进程在CPU上运行的时间。
//参考https://stackoverflow.com/questions/25785243/understanding-time-perf-counter-and-time-process-time
2.4 使用timeit.Timer类来进行时间计算
timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
;
分开time.perf_counter()
2.4.1 Timer的timeit方法
timeit(number=1000000)
将构造函数中的stmt语句执行number遍
2.4.2 Timer的repeat方法
repeat(repeat=5, number=1000000)
重复计时repeat次
2.4.3 Timer的autorange方法
autorange(callback=None)
自动肯定要调用多少次timeit(),它调用timeit()重复使总时间>=0.2秒,返回最终的循环数(循环数,该循环数所需的时间)。它调用timeit()的次数取自从序列1,2,5,10,20,50...,.直到所用的时间至少是0.2秒。
若是callback不为空,在每次调用完都会执行callback(number, time_taken)
2.4.4 Timer的print_exc方法
print_exc(file=None)
打印错误信息,默认打印到sys.stderr
示例:
2.4.5 Timer综合使用
程序计算阶乘,在初始构造时,传入的参数stmt为factorial
是每次都执行的语句,而from test_timeit import factorial;x=n;x += 10;
只在初始化时执行一次,语句间使用了;
做为分隔,同时还传入了全局变量n
。以后打印输出执行1000遍的时间,然后打印重复10次、每次执行10000遍的时间,以后经过构造lambda表达式做为callback,使用autorange自动执行,直到时间达到0.2s,每次执行到必定的次数都会调用callback,打印number和time_taken。具体代码以下:
import timeit def factorial(n): if n == 1: return 1 return n * factorial(n-1) if __name__ == "__main__": n = 10 t = timeit.Timer("factorial(x)", "from test_timeit import factorial;x=n;x += 10;", globals={'n': n}) print(t.timeit(1000)) print(t.repeat(10, 10000)) callback = lambda number, time_taken: print("number:%d, time_taken:%f"%(number, time_taken)) t.autorange(callback)
python3.7的timeit使用体验比python2的timeit好,实际上除了timeit以外的其它不少方面也是,如今的学习之路打算所有转向python3了。
返回目录