Python | Tools | 一遍就能学会使用Python效率监测工具cProfile(Odoo12同样适用)

Python | Tools | 一遍就能学会使用Python效率监测工具cProfile

1. 什么是cProfile&Profile?

  1. cProfile是使用C语言开发的Python方法分析器函数。
  2. Profile是参考cProfile以纯Python语言开发的分析函数,正因如此,ProfilecProfile在执行中更加拉胯
  3. 两者分析完函数执行后,都会使用pstats模块结构化输出分析结果。

2. cProfile操作与介绍

  1. 举个例子

    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排好数据再进行输出,若依如果需要个性化输出类型的话,可以继续往下看:

3. 主动调用函数,避免字符串传入函数调用

  1. 首先看看run()到底执行了什么?

    在这里插入图片描述
    从上面的调用流程分析上我们可以得出结论:

    1. 实际在做收集信息的就是class Profile,虽然再往下调用还是_lsprof中的Profiler。
    2. 上下文传入后,使用的信息收集开关是Profile的enable()和disable()。
    3. 收集到的信息使用pstats进行处理并输出,因为如果提供了文件路径还会写文件,所以最后的输出都是以IO数据流的方式在进行。
    4. 其中可以按照一定的key进行收集信息的输出排序;



  2. 改造方法
    从得到的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的位置上我们可以自定义更多的内容,当然我们也可以使用最上面简单的只传输一个字符串包裹的函数,但是肯定不如这里的可控性更高。

  3. Return测试方法的结果
    说了这么久,还是只能对某个流程或者某个方法进行效率监控,如果测试完这个方法的效率后还需要将被调用的方法返回值return回去,怎么办?

    1. 将cprofile和pstats的代码部分放在业务代码中进行使用,并在do somethings中添加return值的接收并最后将结果返回。
    2. 装饰器
      装饰器一劳永逸,而第一种写在业务代码中时间长了会导致代码难以维护,且与业务逻辑也没什么关系,所以我们还是选择装饰器。
    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
    
  4. 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
    

当方法运行后会有如下统计:
在这里插入图片描述

  1. 阅读文档 - cProfile文档
    基于不同的python版本,可能有不同的功能,记得看看自己的python版本是多少,然后看看有什么特殊用法,上面的内容基本上是共通的,其它的比如python3.9加了上下管理器,就可以直接with调用了,简洁省事。
    import cProfile
    def func():
        pass
    
    with cProfile.Profile() as pr:
        func()
    





🎉如果对你有所帮助,可以点赞、关注、收藏起来,不然下次就找不到了🎉


【点赞】⭐️⭐️⭐️⭐️⭐️
【关注】⭐️⭐️⭐️⭐️⭐️
【收藏】⭐️⭐️⭐️⭐️⭐️

Thanks for watching.
Kenny

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特本特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值