博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python性能优化之函数执行时间分析
阅读量:7175 次
发布时间:2019-06-29

本文共 4110 字,大约阅读时间需要 13 分钟。

最近发现项目API请求比较慢,通过抓包发现主要是response时间太长,于是就开始进行优化工作。优化工作的关键一步是定位出问题的瓶颈,对于优化速度来说,从优化函数执行时间这个维度去切入是一个不错的选择。

本文侧重分析,不展开如何优化

利器

工欲善其事,必先利其器,我们需要一套方便高效的工具记录函数运行时间。说是一套工具,但对于一个简单项目或者日常开发来说,实现一个工具类足矣,由于实现比较简单,直接上代码:

from functools import wrapsimport cProfilefrom line_profiler import LineProfilerimport timedef func_time(f):    """    简单记录执行时间    :param f:    :return:    """    @wraps(f)    def wrapper(*args, **kwargs):        start = time.time()        result = f(*args, **kwargs)        end = time.time()        print f.__name__, 'took', end - start, 'seconds'        return result    return wrapperdef func_cprofile(f):    """    内建分析器    """    @wraps(f)    def wrapper(*args, **kwargs):        profile = cProfile.Profile()        try:            profile.enable()            result = f(*args, **kwargs)            profile.disable()            return result        finally:            profile.print_stats(sort='time')    return wrappertry:    from line_profiler import LineProfiler    def func_line_time(follow=[]):        """        每行代码执行时间详细报告        :param follow: 内部调用方法        :return:        """        def decorate(func):            @wraps(func)            def profiled_func(*args, **kwargs):                try:                    profiler = LineProfiler()                    profiler.add_function(func)                    for f in follow:                        profiler.add_function(f)                    profiler.enable_by_count()                    return func(*args, **kwargs)                finally:                    profiler.print_stats()            return profiled_func        return decorateexcept ImportError:    def func_line_time(follow=[]):        "Helpful if you accidentally leave in production!"        def decorate(func):            @wraps(func)            def nothing(*args, **kwargs):                return func(*args, **kwargs)            return nothing        return decorate复制代码

原始代码可以参考

如下,实现了3个装饰器函数func_time,func_cprofile,func_line_time,分别对应

  1. 简单输出函数的执行时间
  2. 利用python自带的内置分析包cProfile 分析,它主要统计函数调用以及每个函数所占的cpu时间
  3. 利用开源项目,它可以统计每行代码的执行次数和执行时间。

使用说明

我们以一个简单的循环例子来说明一下,

def test():    for x in range(10000000):        print x复制代码
  • func_time

关于func_time我觉得没什么好说的,就是简单输出下函数调用时间,这个在我们粗略统计函数执行时间的时候可以使用

如下:

@func_timedef test():    for x in range(10000000):        print x# 输出test took 6.10190296173 seconds复制代码
  • func_cprofile

cProfile是python内置包,基于lsprof的用C语言实现的扩展应用,运行开销比较合理,没有外部依赖,适合做快速的概要测试

@func_cprofiledef test(): for x in range(10000000):     print x# 输出      3 function calls in 6.249 secondsOrdered by: internal timencalls  tottime  percall  cumtime  percall filename:lineno(function)     1    6.022    6.022    6.249    6.249 test.py:41(test)     1    0.227    0.227    0.227    0.227 {range}     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}复制代码

输出说明:

单位为秒

  1. 第一行告诉我们一共有3个函数被调用。

    正常开发过程,第一行更多是输出类似194 function calls (189 primiive calls) in 0.249 seconds,(189 primiive calls)表示189个是原生(primitive)调用,表明这些调用不涉及递归

  2. ncalls表示函数的调用次数,如果这一列有两个数值,表示有递归调用,第一个是总调用次数,第二个是原生调用次数。

  3. tottime是函数内部消耗的总时间(不包括调用其他函数的时间)。

  4. percall是tottime除以ncalls,表示每次调用平均消耗时间。

  5. cumtime是之前所有子函数消耗时间的累积和。

  6. percall是cumtime除以原生调用的数量,表示该函数调用时,每个原生调用的平均消耗时间。

  7. filename:lineno(function)为被分析函数所在文件名、行号、函数名。

  • func_line_time

可以生成非常直接和详细的报告,但它系统开销很大,会比实际执行时间慢一个数量级

@func_line_time()def test():    for x in range(10000000):        print x# 输出Timer unit: 1e-06 sTotal time: 14.4183 sFile: /xx/test.pyFunction: test at line 41Line #      Hits         Time  Per Hit   % Time  Line Contents==============================================================    41                                           @func_line_time()    42                                           def test():    43  10000001    4031936.0      0.4     28.0      for x in range(10000000):    44  10000000   10386347.0      1.0     72.0          print x复制代码

输出说明:

单位为微秒

  1. Total Time:测试代码的总运行时间

  2. Line:代码行号

  3. Hits:表示每行代码运行的次数

  4. Time:每行代码运行的总时间

  5. Per Hits:每行代码运行一次的时间

  6. % Time:每行代码运行时间的百分比

总结

日常开发中,可以使用func_time,func_cprofile做基本检查,定位大致问题,使用func_line_time进行更细致的深入检查。

注:func_line_time 还可以检查函数内部调用的函数执行时间,通过follow参数指定对应的内部调用的函数声明即可,该参数是个数组,也就是说可以检查多个内部调用的函数

转载地址:http://eebzm.baihongyu.com/

你可能感兴趣的文章
1、JUC--volatile 关键字-内存可见性
查看>>
管理Linux日志文件工具
查看>>
Windows下bat脚本判断端口是否可用
查看>>
oracle创建表
查看>>
解决 Eclipse build workspace 慢,validation javascript 更慢的问题
查看>>
jquery ajax验证用户名是否存在(后台spring mvc)
查看>>
solaris x86安装ORACLE 11.2.0.3软件时因SWAP不足报错: INFO: ld: fatal: mmap anon failed
查看>>
paoding分词
查看>>
jquery动态创建form表单
查看>>
hdu 5361 2015多校联合训练赛#6 最短路
查看>>
php中PHPMailer发送带附件的电子邮件方法
查看>>
DOCKER_HOST have a weird tcp
查看>>
Redisclient连接方式Hiredis简单封装使用,连接池、屏蔽连接细节
查看>>
微信支付:价格问题:如果支付金额是单位是分,不能带小数点
查看>>
微软小冰发威,招摇人工智能
查看>>
HDOJ 4009 Transfer water 最小树形图
查看>>
Maven的SSH搭建以及部署
查看>>
常用函数 __MySQL必知必会
查看>>
JavaScript--语法4--函数1
查看>>
Spring Boot注解(annotation)列表
查看>>