python性能分析(一)——使用timeit给你的程序打个表吧
前言
我们可以通过查看程序核心算法的代码,得知核心算法的渐进上界或者下界,从而大概估计出程序在运行时的效率,但是这并不够直观,也不一定十分靠谱(在整体程序中仍有一些不可忽略的运行细节在估计时被忽略了),因此在实际评测程序时我们还是需要实际的考量程序的运行时间和瓶颈,最好具体到执行一段代码多少次,执行一段代码花了多少时间,幸好的是Python自带了许多有用的工具,可以帮助我们实现这些要求,下面是一些我在学习中记录的笔记,从简单到复杂介绍了python性能分析的方法,希望我的笔记能帮到您。
注:写作不易,转载请注明出处,谢谢支持~
目录
一、使用timeit计算程序耗时
timeit可以在命令行通过-m指令导入作为脚本运行,也可以在代码内import导入使用,它会将代码执行多遍,然后得出耗时最短的时间是多少,下面是具体的几种使用方式:
注:我在实际测试时使用的是python3.7的环境,在文末,有举例说明python2和python3.7使用时实际上不同的地方
1. 命令行执行
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的时间
1.2 通过添加 -n N
命令可以设置语句执行的次数:
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)
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. 导入使用
2.1 使用timeit.timeit
timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)
参数解释:
- stmt 语句,要执行的表达式,多个语句可以使用
;
分开 - setup 语句,只在第一次初始化时执行的表达式,在之后会跳过
- timer 计时器,默认是
time.perf_counter()
- number 执行次数
- globals 全局变量,需要是个字典
使用实例:
Q&A: 为什么不带number参数会比number=10**5时所费时间更长呢?
- 因为number的默认值是10**6
Q&A: 输出结果的默认单位是什么?
- usec,微秒
通过setup参数,设置初始执行的语句,下例是'x = 2',当重复执行多遍时,'x = 2'只在第一次执行一次。
通过globals携带全局变量
执行多个语句
2.2 使用timeit.repeat,输出的是个列表,包含重复次数个的测试时间
timeit.repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000, globals=None)
参数解释:
- stmt 语句,要执行的表达式,多个语句可以使用
;
分开 - setup 语句,只在第一次初始化时执行的表达式,在之后会跳过
- timer 计时器,默认是
time.perf_counter()
- repeat 重复次数,默认是5
- number 执行次数
- globals 全局变量,需要是个字典
使用起来与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)
- stmt 语句,要执行的表达式,多个语句可以使用
;
分开 - setup 语句,只在第一次初始化时执行的表达式,在之后会跳过
- timer 计时器,默认是
time.perf_counter()
- globals 全局变量,需要是个字典
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)
3. timeit在python3.7和python2中的区别
- python3.7中的timeit命令行执行时默认的重复次数是5,而python2是3
- python2命令行执行时没有-p -u选项
- python2导入执行时,timeit.timeit()没有globals参数
- python2没有autorange()
python3.7的timeit使用体验比python2的timeit好,实际上除了timeit之外的其它很多方面也是,现在的学习之路打算全部转向python3了。
返回目录
python性能分析(一)——使用timeit给你的程序打个表吧的更多相关文章
- 如何进行 Python性能分析,你才能如鱼得水?
[编者按]本文作者为 Bryan Helmig,主要介绍 Python 应用性能分析的三种进阶方案.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 我们应该忽略一些微小的效率提升,几乎在 9 ...
- Python性能分析
Python性能分析 https://www.cnblogs.com/lrysjtu/p/5651816.html https://www.cnblogs.com/cbscan/articles/33 ...
- Python性能分析工具Profile
Python性能分析工具Profile 代码优化的前提是需要了解性能瓶颈在什么地方,程序运行的主要时间是消耗在哪里,对于比较复杂的代码可以借助一些工具来定位,python 内置了丰富的性能分析工具,如 ...
- Python性能分析与优化PDF高清完整版免费下载|百度云盘
百度云盘|Python性能分析与优化PDF高清完整版免费下载 提取码:ubjt 内容简介 全面掌握Python代码性能分析和优化方法,消除性能瓶颈,迅速改善程序性能! 对于Python程序员来说,仅仅 ...
- 如何进行python性能分析?
在分析python代码性能瓶颈,但又不想修改源代码的时候,ipython shell以及第三方库提供了很多扩展工具,可以不用在代码里面加上统计性能的装饰器,也能很方便直观的分析代码性能.下面以我自己实 ...
- Python丨Python 性能分析大全
虽然运行速度慢是 Python 与生俱来的特点,大多数时候我们用 Python 就意味着放弃对性能的追求.但是,就算是用纯 Python 完成同一个任务,老手写出来的代码可能会比菜鸟写的代码块几倍,甚 ...
- Python 性能分析工具简介
Table of Contents 1. 性能分析和调优工具简介 1.1. Context Manager 1.2. Decorator 1.3. 系统自带的time命令 1.4. python ti ...
- Python性能分析指南
http://www.admin10000.com/document/2861.html 尽管并非每个你写的Python程序都需要严格的性能分析,但了解一下Python的生态系统中很多优秀的在你需要做 ...
- Python性能分析指南(未完成)
英文原文:http://www.huyng.com/posts/python-performance-analysis/ 译文:http://www.oschina.net/translate/pyt ...
随机推荐
- Python 安装出错:Setup script exited with error: command 'gcc' failed with exit status 1
退出当前环境: logout (再重新登录进去) yum install python-devel -yyum install libevent-devel -y 把环境更新下yum instal ...
- 【题目】求n以内的素数个数
最近在leetCode上刷提,还是满锻炼人的,为以后面试打基础吧.不多说下面开始. 问题:求[2,n]之间的素数的个数. 来源:leetCode OJ 提示: Let's start with a i ...
- FastDFS client for .net
fastDfs Code: https://code.google.com/p/fastdfs/ FastDfs_Client_DotNet:https://code.google.com/p/fas ...
- SOA架构商城二 框架搭建
1.创建父工程 创建Maven工程pingyougou-parent,选择packaging类型为pom ,在pom.xml文件中添加锁定版本信息dependencyManagement与plugin ...
- SVG学习笔录(二)
一.svg动画SMIL SVG采用的是使用文本来定义图形,这种文档结构非常适合于创建动画.要改变图形的位置.大小和颜色,只需要调整相应的属性就可以了.事实上,SVG有为各种事件处理而专门设计的属性,甚 ...
- [LintCode] Find the Missing Number 寻找丢失的数字
Given an array contains N numbers of 0 .. N, find which number doesn't exist in the array. Example G ...
- OOA/D 01
建筑师一般不会为一栋100层的楼添加一个新的地下室,因为成本太高无疑会失败,但软件系统里提出类似改动需求时,他们通常都不会多想一下,相反他们会说:这只是一个简单的编程问题 可总会有一些看似极难完成.但 ...
- CSS基础问题
1.css引入问题 本来以为css引入是很简单的问题,但是在写demo中,使用连接方式引入就出现了问题,找了半天,还是说一下问题吧. 在引入时没有写rel="stylesheet" ...
- 把本地项目文件上传到github远程仓库的教程
参考廖雪峰的git教程:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 非常详 ...
- iOS-动画之CoreAnimation框架(转载)
一.简介 iOS动画主要是指Core Animation框架.官方使用文档地址为:Core Animation Guide.Core Animation是iOS和macOS平台上负责图形渲染与动画的基 ...