10种检测Python程序运行时间、CPU和内存占用的方法


Posted in Python onApril 01, 2015

在运行复杂的Python程序时,执行时间会很长,这时也许想提高程序的执行效率。但该怎么做呢?

首先,要有个工具能够检测代码中的瓶颈,例如,找到哪一部分执行时间比较长。接着,就针对这一部分进行优化。

同时,还需要控制内存和CPU的使用,这样可以在另一方面优化代码。

因此,在这篇文章中我将介绍7个不同的Python工具,来检查代码中函数的执行时间以及内存和CPU的使用。
1. 使用装饰器来衡量函数执行时间

有一个简单方法,那就是定义一个装饰器来测量函数的执行时间,并输出结果:
 

import time
from functools import wraps
 
def fn_timer(function):
  @wraps(function)
  def function_timer(*args, **kwargs):
    t0 = time.time()
    result = function(*args, **kwargs)
    t1 = time.time()
    print ("Total time running %s: %s seconds" %
        (function.func_name, str(t1-t0))
        )
    return result
  return function_timer

接着,将这个装饰器添加到需要测量的函数之前,如下所示:
 

@fn_timer
def myfunction(...):
...

例如,这里检测一个函数排序含有200万个随机数字的数组所需的时间:
 

@fn_timer
def random_sort(n):
  return sorted([random.random() for i in range(n)])
 
if __name__ == "__main__":
  random_sort(2000000)

执行脚本时,会看到下面的结果:
 

Total time running random_sort: 1.41124916077 seconds

2. 使用timeit模块

另一种方法是使用timeit模块,用来计算平均时间消耗。

执行下面的脚本可以运行该模块。

python -m timeit -n 4 -r 5 -s "import timing_functions" "timing_functions.random_sort(2000000)"

这里的timing_functions是Python脚本文件名称。

在输出的末尾,可以看到以下结果:
 

4 loops, best of 5: 2.08 sec per loop

这表示测试了4次,平均每次测试重复5次,最好的测试结果是2.08秒。

如果不指定测试或重复次数,默认值为10次测试,每次重复5次。
3. 使用Unix系统中的time命令

然而,装饰器和timeit都是基于Python的。在外部环境测试Python时,unix time实用工具就非常有用。

运行time实用工具:
 

$ time -p python timing_functions.py

输出结果为:
 

Total time running random_sort: 1.3931210041 seconds
real 1.49
user 1.40
sys 0.08

第一行来自预定义的装饰器,其他三行为:

  •     real表示的是执行脚本的总时间
  •     user表示的是执行脚本消耗的CPU时间。
  •     sys表示的是执行内核函数消耗的时间。

注意:根据维基百科的定义,内核是一个计算机程序,用来管理软件的输入输出,并将其翻译成CPU和其他计算机中的电子设备能够执行的数据处理指令。

因此,Real执行时间和User+Sys执行时间的差就是消耗在输入/输出和系统执行其他任务时消耗的时间。
4. 使用cProfile模块

如果想知道每个函数和方法消耗了多少时间,以及这些函数被调用了多少次,可以使用cProfile模块。
 

$ python -m cProfile -s cumulative timing_functions.py

现在可以看到代码中函数的详细描述,其中含有每个函数调用的次数,由于使用了-s选项(累加),最终结果会根据每个函数的累计执行时间排序。

10种检测Python程序运行时间、CPU和内存占用的方法

读者会发现执行脚本所需的总时间比以前要多。这是由于测量每个函数的执行时间这个操作本身也是需要时间。
5. 使用line_profiler模块

line_profiler模块可以给出执行每行代码所需占用的CPU时间。

首先,安装该模块:
 

$ pip install line_profiler

接着,需要指定用@profile检测哪个函数(不需要在代码中用import导入模块):
 

@profile
def random_sort2(n):
  l = [random.random() for i in range(n)]
  l.sort()
  return l
 
if __name__ == "__main__":
  random_sort2(2000000)

最好,可以通过下面的命令获得关于random_sort2函数的逐行描述。
 

$ kernprof -l -v timing_functions.py

其中-l表示逐行解释,-v表示表示输出详细结果。通过这种方法,我们看到构建数组消耗了44%的计算时间,而sort()方法消耗了剩余的56%的时间。

10种检测Python程序运行时间、CPU和内存占用的方法

同样,由于需要检测执行时间,脚本的执行时间更长了。
6. 使用memory_profiler模块

memory_profiler模块用来基于逐行测量代码的内存使用。使用这个模块会让代码运行的更慢。

安装方法如下:

pip install memory_profiler

另外,建议安装psutil包,这样memory_profile会运行的快一点:
 

$ pip install psutil

与line_profiler相似,使用@profile装饰器来标识需要追踪的函数。接着,输入:
 

$ python -m memory_profiler timing_functions.py

脚本的执行时间比以前长1或2秒。如果没有安装psutil包,也许会更长。

10种检测Python程序运行时间、CPU和内存占用的方法

从结果可以看出,内存使用是以MiB为单位衡量的,表示的mebibyte(1MiB = 1.05MB)。
7. 使用guppy包

最后,通过这个包可以知道在代码执行的每个阶段中,每种类型(str、tuple、dict等)分别创建了多少对象。

安装方法如下:
 

$ pip install guppy

接着,将其添加到代码中:
 

from guppy import hpy
 
def random_sort3(n):
  hp = hpy()
  print "Heap at the beginning of the functionn", hp.heap()
  l = [random.random() for i in range(n)]
  l.sort()
  print "Heap at the end of the functionn", hp.heap()
  return l
 
if __name__ == "__main__":
  random_sort3(2000000)

运行代码:
 

$ python timing_functions.py

可以看到输出结果为:

10种检测Python程序运行时间、CPU和内存占用的方法

通过在代码中将heap()放置在不同的位置,可以了解到脚本中的对象创建和删除操作的流程。

如果想学习更多关于Python代码速度优化方面的知识,我建议你去读这本书《High Performance Python: Practical Performant Programming for Humans, september 2014.》

希望这篇文章能偶帮到你!^_^

Python 相关文章推荐
新手该如何学python怎么学好python?
Oct 07 Python
python中getattr函数使用方法 getattr实现工厂模式
Jan 20 Python
使用Python进行新浪微博的mid和url互相转换实例(10进制和62进制互算)
Apr 25 Python
pip 错误unused-command-line-argument-hard-error-in-future解决办法
Jun 01 Python
python开发之thread实现布朗运动的方法
Nov 11 Python
Python的包管理器pip更换软件源的方法详解
Jun 20 Python
教你用Python脚本快速为iOS10生成图标和截屏
Sep 22 Python
python获取程序执行文件路径的方法(推荐)
Apr 26 Python
django ajax json的实例代码
May 29 Python
详解在Python中以绝对路径或者相对路径导入文件的方法
Aug 30 Python
python调用HEG工具批量处理MODIS数据的方法及注意事项
Feb 18 Python
Python虚拟环境virtualenv是如何使用的
Jun 20 Python
深入Python解释器理解Python中的字节码
Apr 01 #Python
Python中的defaultdict模块和namedtuple模块的简单入门指南
Apr 01 #Python
Python进行数据科学工作的简单入门教程
Apr 01 #Python
10个易被忽视但应掌握的Python基本用法
Apr 01 #Python
用Python制作检测Linux运行信息的工具的教程
Apr 01 #Python
用Python的pandas框架操作Excel文件中的数据教程
Mar 31 #Python
Python实现国外赌场热门游戏Craps(双骰子)
Mar 31 #Python
You might like
mysql 搜索之简单应用
2007/04/27 PHP
php 分页函数multi() discuz
2009/06/21 PHP
php实现在服务器上创建目录的方法
2015/03/16 PHP
thinkPHP5.0框架API优化后的友好性分析
2017/03/17 PHP
PHP实现防止表单重复提交功能【基于token验证】
2018/05/24 PHP
微信公众号实现扫码获取微信用户信息(网页授权)
2019/04/09 PHP
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
Javascript YUI 读码日记之 YAHOO.util.Dom - Part.2 0
2008/03/22 Javascript
图片在浏览器中底部对齐 解决方法之一
2011/11/30 Javascript
js创建子窗口并且回传值示例代码
2013/07/02 Javascript
jQuery的框架介绍
2016/05/11 Javascript
BootStrap智能表单实战系列(九)表单图片上传的支持
2016/06/13 Javascript
jQuery实现可拖拽3D万花筒旋转特效
2017/01/03 Javascript
详解升级react-router 4 踩坑指南
2017/08/14 Javascript
在 webpack 中使用 ECharts的实例详解
2018/02/05 Javascript
Vue封装一个简单轻量的上传文件组件的示例
2018/03/21 Javascript
你可能不知道的CORS跨域资源共享
2019/03/13 Javascript
webpack DllPlugin xxx is not defined解决办法
2019/12/13 Javascript
[49:31]TFT vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
Python日志模块logging简介
2015/04/13 Python
在Python的Flask框架下收发电子邮件的教程
2015/04/21 Python
Pycharm设置utf-8自动显示方法
2019/01/17 Python
TensorFlow tf.nn.conv2d实现卷积的方式
2020/01/03 Python
Django用户登录与注册系统的实现示例
2020/06/03 Python
python 录制系统声音的示例
2020/12/21 Python
python lambda的使用详解
2021/02/26 Python
html5视频常用API接口的实战示例
2020/03/20 HTML / CSS
实习销售业务员自我鉴定
2013/09/21 职场文书
师范生自荐信模板
2014/05/28 职场文书
镇班子对照检查材料思想汇报
2014/09/24 职场文书
生日赠语
2015/06/23 职场文书
2019预备党员转正申请书模板2篇!
2019/08/07 职场文书
MySQL系列之五 视图、存储函数、存储过程、触发器
2021/07/02 MySQL
详解解Django 多对多表关系的三种创建方式
2021/08/23 Python
vue实现滑动解锁功能
2022/03/03 Vue.js
深入理解pytorch库的dockerfile
2022/06/10 Python