Python | Tools | 一遍就能学会使用Python效率监测工具cProfile
1. 什么是cProfile&Profile?
cProfile
是使用C语言
开发的Python方法分析器函数。Profile
是参考cProfile以纯Python语言
开发的分析函数,正因如此,Profile
比cProfile
在执行中更加拉胯
。- 两者分析完函数执行后,都会使用pstats模块结构化输出分析结果。
2. cProfile操作与介绍
-
举个例子
import cProfile def func(): pass cProfile.run('func()')
import cProfile def func(age=None): if age > 1: age -= 1 func(age) cProfile.run('func(3)')
参数说明:ncalls
- 一个数字Primitive calls形式
1
,代表调用次数,并不存在递归; - 两个数字All calls / Primitive calls形式
3/1
All calls
代当前原生方法
和内部递归方法
调用次数之和Primitive calls
代表当前原生方法
被调用了几次
tottime
- 调用函数实际执行时间(不计算内部任何子函数的调用时间)
percall
- tottime / ncalls (参考价值不大)
cumtime
- 函数和内部子函数调用的总时间
percall
- 调用总时间 cumtime / ncall的原始调用次数
filename:lineno(function)
:- 函数调用名与行号位置
目前来看,我们的测试结果默认通过
pstats
排好数据再进行输出,若依如果需要个性化输出类型的话,可以继续往下看: - 一个数字Primitive calls形式
3. 主动调用函数,避免字符串传入函数调用
-
首先看看
run()
到底执行了什么?
从上面的调用流程分析上我们可以得出结论:- 实际在做收集信息的就是class Profile,虽然再往下调用还是_lsprof中的Profiler。
- 上下文传入后,使用的信息收集开关是Profile的enable()和disable()。
- 收集到的信息使用pstats进行处理并输出,因为如果提供了文件路径还会写文件,所以最后的输出都是以IO数据流的方式在进行。
- 其中可以按照一定的key进行收集信息的输出排序;
-
改造方法
从得到的4点结论,为了避免传入的时候穿了个字符串的函数调用,增加主动可控性,我们可以拆解开来使用:import cProfile, pstats import io s = io.StringIO() # 收集 pr = cProfile.Profile() pr.enable() # do somethings. pr.disable() # 排序 # sortby支持-1、0、1、2;也支持SortKey的枚举值;也支持枚举值中的具体值(这些可以看下源代码) sortby = 'cumulative' ps = pstats.Stats(pr, stream=s).sort_stats(sortby) # 输出 ps.print_stats() """ 有不少同学咨询sortby到底支持什么,这里简单备注一下: class SortKey(str, Enum): CALLS = 'calls', 'ncalls' CUMULATIVE = 'cumulative', 'cumtime' FILENAME = 'filename', 'module' LINE = 'line' NAME = 'name' NFL = 'nfl' PCALLS = 'pcalls' STDNAME = 'stdname' TIME = 'time', 'tottime' 1. 第一类:数字 -1 => stdname 0 => calls 1 => time 2 => cumulative 2. 第二类:枚举值 CALLS CUMULATIVE FILENAME LINE NAME NFL PCALLS STDNAME TIME 3. 第三类:实际值 即枚举值字典对应的values:calls、ncalls、cumulative、cumtime... """
在
do somethings
的位置上我们可以自定义更多的内容,当然我们也可以使用最上面简单的只传输一个字符串包裹的函数,但是肯定不如这里的可控性更高。 -
Return测试方法的结果
说了这么久,还是只能对某个流程或者某个方法进行效率监控,如果测试完这个方法的效率后还需要将被调用的方法返回值return回去,怎么办?- 将cprofile和pstats的代码部分放在业务代码中进行使用,并在do somethings中添加return值的接收并最后将结果返回。
- 装饰器
装饰器一劳永逸,而第一种写在业务代码中时间长了会导致代码难以维护,且与业务逻辑也没什么关系,所以我们还是选择装饰器。
def time_test(func): """ 测试方法执行时间装饰器 @time_test def func(self): pass :return: """ def inner_func(*args, **kwargs): import cProfile, pstats import io pr = cProfile.Profile() pr.enable() return_value = func(*args, **kwargs) pr.disable() s = io.StringIO() sortby = 'cumulative' ps = pstats.Stats(pr, stream=s).sort_stats(sortby) ps.print_stats() print(s.getvalue()) return return_value return inner_func
这样,我们在使用的时候只需要在对应的方法上添加上装饰器即可:
@time_test def time_test_func(self, *args, **kwargs): pass
-
Odoo中怎么使用
我们将此方法放在一个公共的python文件中,测试某个方法执行次数和时间节点的时候可以直接引入,并加入装饰器如下:# dy_fin_pf是我的测试module名 # common_pf_functions 是我创建的公共方法py文件名 from odoo.addons.dy_fin_pf.models.common_pf_functions import time_test as time_test class OdooClassInstince(models.Model): ... @api.model @time_test def time_test_func(self, *args, **kwargs): pass
当方法运行后会有如下统计:
- 阅读文档 - cProfile文档
基于不同的python版本,可能有不同的功能,记得看看自己的python版本是多少,然后看看有什么特殊用法,上面的内容基本上是共通的,其它的比如python3.9加了上下管理器,就可以直接with调用了,简洁省事。import cProfile def func(): pass with cProfile.Profile() as pr: func()
🎉如果对你有所帮助,可以点赞、关注、收藏起来,不然下次就找不到了🎉
【点赞】⭐️⭐️⭐️⭐️⭐️
【关注】⭐️⭐️⭐️⭐️⭐️
【收藏】⭐️⭐️⭐️⭐️⭐️
Thanks for watching.
–Kenny