性能分析工具的使用

cProfile

  1. 介绍

    它是一种确定性的性能分析器,提供了一组API帮助开 发者收集Python程序运行的信息,更确切地说,是统计每个函数消耗的 CPU时间。同时它还提供了其他细节,比如函数被调用的次数。 cProfile只测量CPU时间,并不关心内存消耗和其他与内存相关的信 息统计。尽管如此,它是代码优化过程中一个很不错的起点,因为大多 数时候,这个分析工具都会快速地为我们提供一组优化方案。 cProfile不需要安装,因为它是语言自带的一部分。要使用它,直接 导入cProfile包即可。
    
  2. 例子

    • 简单调用

      import cProfile
      import recProfile.run('re.compile("foo|bar")')
      
    • 调用函数

      def test():pass
      cProfile.run('test()')
      
    • 命令行使用

      # 直接把分析结果打印到控制台
      python -m cProfile test.py
      # 把分析结果保存到文件中
      python -m cProfile -o result.out test.py
      # 增加排序方式
      python -m cProfile -o result.out -s cumulative test.py
      
    • 当命令被调用时,会执行下面这个函数:

      exec(command, __main__.__dict__, __main__.__dict__)
      

      故这种写法会报错

      import cProfile
      def runRe():import recProfile.run('re.compile("foo|bar")') #改为它即可正常运行cProfile.runctx('re.compile("foo|bar")', None, locals())
      runRe()
      
  3. Stats

    • 利用stats打印函数的调用链

      import cProfile
      import pstats
      def runRe():import rere.compile("foo|bar")prof = cProfile.Profile()
      prof.enable()
      runRe()
      prof.create_stats()
      p = pstats.Stats(prof)
      p.print_callers()
      
    • 用来分析cProfile输出文件xxx.out

      import pstats# 创建Stats对象
      p = pstats.Stats("result.out")# strip_dirs(): 去掉无关的路径信息
      # sort_stats(): 排序,支持的方式和上述的一致
      # print_stats(): 打印分析结果,可以指定打印前几行# 和直接运行cProfile.run("test()")的结果是一样的
      p.strip_dirs().sort_stats(-1).print_stats()# 按照函数名排序,只打印前3行函数的信息, 参数还可为小数,表示前百分之几的函数信息
      p.strip_dirs().sort_stats("name").print_stats(3)# 按照运行时间和函数名进行排序
      p.strip_dirs().sort_stats("cumulative", "name").print_stats(0.5)# 如果想知道有哪些函数调用了sum_num
      p.print_callers(0.5, "sum_num")# 查看test()函数中调用了哪些函数
      p.print_callees("test")
      
  4. cProfile其它的方法

    • enable():表示开始收集性能分析数据。

    • disable():表示停止收集性能分析数据。

    • create_stats():表示停止收集数据,并为已收集的数据创建

    stats对象。

    • print_stats(sort=-1):创建一个stats对象,打印分析结果。

    • dump_stats(filename):把当前性能分析的内容写进一个文件。

    • run(cmd):和之前介绍过的run函数相同。

    • runctx(cmd, globals, locals):和之前介绍过的runctx函数

    相同。

    • runcall(func, *args, **kwargs):收集被调用函数func的性

    能分析信息

  5. 运行结果如下(为以上简单调用的结果)

            214 function calls (207 primitive calls) in 0.000 secondsOrdered by: standard namencalls  tottime  percall  cumtime  percall filename:lineno(function)1    0.000    0.000    0.000    0.000 <string>:1(<module>)2    0.000    0.000    0.000    0.000 enum.py:283(__call__)2    0.000    0.000    0.000    0.000 enum.py:525(__new__)1    0.000    0.000    0.000    0.000 enum.py:815(__and__)1    0.000    0.000    0.000    0.000 re.py:232(compile)1    0.000    0.000    0.000    0.000 re.py:271(_compile)1    0.000    0.000    0.000    0.000 sre_compile.py:249(_compile_charset)1    0.000    0.000    0.000    0.000 sre_compile.py:276(_optimize_charset)2    0.000    0.000    0.000    0.000 sre_compile.py:453(_get_iscased)1    0.000    0.000    0.000    0.000 sre_compile.py:461(_get_literal_prefix)1    0.000    0.000    0.000    0.000 sre_compile.py:492(_get_charset_prefix)1    0.000    0.000    0.000    0.000 sre_compile.py:536(_compile_info)2    0.000    0.000    0.000    0.000 sre_compile.py:595(isstring)1    0.000    0.000    0.000    0.000 sre_compile.py:598(_code)3/1    0.000    0.000    0.000    0.000 sre_compile.py:71(_compile)1    0.000    0.000    0.000    0.000 sre_compile.py:759(compile)3    0.000    0.000    0.000    0.000 sre_parse.py:111(__init__)7    0.000    0.000    0.000    0.000 sre_parse.py:160(__len__)18    0.000    0.000    0.000    0.000 sre_parse.py:164(__getitem__)7    0.000    0.000    0.000    0.000 sre_parse.py:172(append)3/1    0.000    0.000    0.000    0.000 sre_parse.py:174(getwidth)1    0.000    0.000    0.000    0.000 sre_parse.py:224(__init__)8    0.000    0.000    0.000    0.000 sre_parse.py:233(__next)2    0.000    0.000    0.000    0.000 sre_parse.py:249(match)6    0.000    0.000    0.000    0.000 sre_parse.py:254(get)1    0.000    0.000    0.000    0.000 sre_parse.py:286(tell)1    0.000    0.000    0.000    0.000 sre_parse.py:417(_parse_sub)2    0.000    0.000    0.000    0.000 sre_parse.py:475(_parse)1    0.000    0.000    0.000    0.000 sre_parse.py:76(__init__)2    0.000    0.000    0.000    0.000 sre_parse.py:81(groups)1    0.000    0.000    0.000    0.000 sre_parse.py:903(fix_flags)1    0.000    0.000    0.000    0.000 sre_parse.py:919(parse)1    0.000    0.000    0.000    0.000 {built-in method _sre.compile}1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}25    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}29/26    0.000    0.000    0.000    0.000 {built-in method builtins.len}2    0.000    0.000    0.000    0.000 {built-in method builtins.max}9    0.000    0.000    0.000    0.000 {built-in method builtins.min}6    0.000    0.000    0.000    0.000 {built-in method builtins.ord}48    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}5    0.000    0.000    0.000    0.000 {method 'find' of 'bytearray' objects}1    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
    
    • 第一行告诉我们一共有214个函数调用被监控,其中207个是原生 (primitive)调用,表明这些调用不涉及递归。
    • ncalls表示函数调用的次数。如果在这一列中有两个数值,就表 示有递归调用。第二个数值是原生调用的次数,第一个数值是总调 用次数。这个数值可以帮助识别潜在的bug(当数值大得异乎寻常 时可能就是bug),或者可能需要进行内联函数扩展(inline expansion)的位置。
    • tottime是函数内部消耗的总时间(不包括调用其他函数的时 间)。这列信息可以帮助开发者找到可以进行优化的、运行时间较 长的循环。
    • percall是tottime除以ncalls,表示一个函数每次调用的平均消 耗时间。
    • cumtime是之前所有子函数消耗时间的累计和(也包含递归调用时 间)。这个数值可以帮助开发者从整体视角识别性能问题,比如算 法选择错误。
    • 另一个percall是用cumtime除以原生调用的数量,表示到该函数 调用时,每个原生调用的平均消耗时间。
    • filename:lineno(function)显示了被分析函数所在的文件名、 行号、函数名。
  6. cProfile工具的局限

    不存在透明的性能分析器。也就是说,即使像cProfile这样消耗极小 的性能分析器,仍然会对代码实际的性能造成影响。当一个事件被触发 时,事件实际发生的时间相比性能分析器查询到的系统内部时钟的时间,还是会有一些延迟。另外,当程序计数器离开性能分析器代码,回 到用户代码中继续执行时,程序也会出现滞后。 除了这些之外,作为计算机内部的任何一段代码,内部时钟都有一个精 度范围,任何小于这个精度的测量值都会被忽略。也就是说,如果进行 性能分析的代码含有许多递归调用,或者一个函数需要调用许多函数, 开发者就应该对这个函数做特殊处理,因为测量误差不断地累计最终会 变得非常显著。
    

Line Profiler

  1. 不为python sdk自带,需要手动下载

    pip install line_profiler
    
  2. 我们通过cProfile定位到了具体的耗时函数,下面就需要具体定位瓶颈出在哪行代码,这个时候就到了Line Profiler出场的时候了。与cProfile相比,Line Profiler的结果更加直观,它可以告诉你一个函数中每一行的耗时。

  3. 用法

    • 定义一个测试文件test.py

      # demo.py
      @profile
      def foo():task = []for a in range(0, 101):for b in range(0, 101):if a + b == 100:task.append((a, b))return task@profile
      def run():for item in foo():passif __name__ == '__main__':run()
      
    • 运行

      kernprof -l -v test.py #-l逐行分析
      
    • 结果

      Wrote profile results to test.py.lprof
      Timer unit: 1e-06 sTotal time: 0.0051146 s
      File: test.py
      Function: foo at line 1Line #      Hits         Time  Per Hit   % Time  Line Contents
      ==============================================================1                                           @profile2                                           def foo():3         1          0.4      0.4      0.0      task = []4       102         22.6      0.2      0.4      for a in range(0, 101):5     10302       2317.9      0.2     45.3          for b in range(0, 101):6     10201       2738.2      0.3     53.5              if a + b == 100:7       101         35.2      0.3      0.7                  task.append((a, b))8         1          0.3      0.3      0.0      return taskTotal time: 0.0089912 s
      File: test.py
      Function: run at line 11Line #      Hits         Time  Per Hit   % Time  Line Contents
      ==============================================================11                                           @profile12                                           def run():13       102       8970.8     87.9     99.8      for item in foo():14       101         20.4      0.2      0.2          pass
      

      Timer unit: 1e-06 s:时间单位;
      Total time: 0.004891 s:总时间;
      Hit:代码运行次数;
      %Time:代码占了它所在函数的消耗的时间百分比,通常直接看这一列。

memory_profile

  • 下载

    pip3 install memory_profiler
    
  • 测试代码

    def sum_num(max_num):total = 0for i in range(max_num):total += ireturn total@profile
    def test():total = 0for i in range(40000):total += it1 = sum_num(10000000)t2 = sum_num(200000)t3 = sum_num(300000)t4 = sum_num(400000)t5 = sum_num(500000)test2()return totaldef test2():total = 0for i in range(40000):total += it6 = sum_num(600000)t7 = sum_num(700000)return totaltest()
    

    python -m memory_profiler test.py

  • 运行结果

    Filename: test.pyLine #    Mem usage    Increment  Occurences   Line Contents
    ============================================================8   39.660 MiB   39.660 MiB           1   @profile9                                         def test():10   39.660 MiB    0.000 MiB           1       total = 011   39.660 MiB    0.000 MiB       40001       for i in range(40000):12   39.660 MiB    0.000 MiB       40000           total += i1314   39.660 MiB    0.000 MiB           1       t1 = sum_num(10000000)15   39.660 MiB    0.000 MiB           1       t2 = sum_num(200000)16   39.660 MiB    0.000 MiB           1       t3 = sum_num(300000)17   39.660 MiB    0.000 MiB           1       t4 = sum_num(400000)18   39.660 MiB    0.000 MiB           1       t5 = sum_num(500000)19   39.660 MiB    0.000 MiB           1       test2()2021   39.660 MiB    0.000 MiB           1       return total
    

    Mem usage : 运行内存大小;
    Increment : 运行当前代码后,增加的内存。

装饰器捕获函数运行时间

  • 模板

    import time
    from functools import wraps
    def simple_profiling(fn):@wraps(fn)  # 对外暴露调用装饰器函数的函数名和docstringdef wrapped(*args, **kwargs):t1 = time.time()result = fn(*args, **kwargs)t2 = time.time()print ("@simple_profiling:" + fn.func_name + " took " + str(t2 - t1) + " seconds")return resultreturn wrapped
    @simple_profiling
    def foo(a, b, c)pass
    
  • 优缺点总结

    这个方法的优点是简单,额外开效非常低(大部分情况下可以忽略不计)。但缺点也很明显,除了总用时,没有任何其他信息。

    timeit

    • 该模块为python自带
    • 此模块提供了一种简单的方法来计算一小段 Python 代码的耗时。 它有 命令行界面 以及一个 可调用 方法。 它避免了许多测量时间的常见陷阱。

python性能分析工具总结相关推荐

  1. python性能分析工具模块_python——关于Python Profilers性能分析器

    1. 介绍性能分析器 profiler是一个程序,用来描述运行时的程序性能,并且从不同方面提供统计数据加以表述.Python中含有3个模块提供这样的功能,分别是cProfile, profile和ps ...

  2. python性能分析工具

    1)cProfile cProfile可以嵌入到python代码中执行,比如: import cProfile cProfile.run('foo()', 'foo.out') 查看结果需要pstat ...

  3. cProfile——Python性能分析工具

    Python自带了几个性能分析的模块:profile.cProfile和hotshot,使用方法基本都差不多,无非模块是纯Python还是用C写的.本文介绍cProfile.  例子 import t ...

  4. python性能分析工具_Python Profilers 分析器

    实时用户手册¶ 本节是为 "不想阅读手册" 的用户提供的.它提供了非常简短的概述,并允许用户快速对现有应用程序执行评测. 要分析采用单个参数的函数,可以执行以下操作: import ...

  5. python性能分析工具模块_Python Profilers 分析器

    实时用户手册¶ 本节是为 "不想阅读手册" 的用户提供的.它提供了非常简短的概述,并允许用户快速对现有应用程序执行评测. 要分析采用单个参数的函数,可以执行以下操作: import ...

  6. python输出程序运行时间_叨叨 Python 性能优化工具

    虽然Python是一个"慢慢的"语言,但是不代表我们对性能没有任何的追求,在程序运行过程中,如果发现程序运行时间太长或者内存占用过大,免不了需要对程序的执行过程进行一些监测,找到有 ...

  7. 系统级性能分析工具perf的介绍与使用

    测试环境:Ubuntu16.04 + Kernel:4.4.0-31 apt-get install linux-source cd /usr/src/tools/perf make &&am ...

  8. Linux常用性能分析工具汇总

    文章目录 性能分析工具 top pstree mpstat vmstat pidstat perf proc tcpdump bcc工具箱 cachestat cachetop memleak fil ...

  9. Python 性能剖分工具

    Python 性能剖分工具 眼看着项目即将完成,却被测试人员告知没有通过性能测试,这种情况在开发中屡见不鲜.接下来的工作就是加班加点地找出性能瓶颈,然后进行优化,再进行性能测试,如此这般周而复始直到通 ...

最新文章

  1. hadoop2 作业执行过程之作业提交
  2. java 大臣的旅费_PREV-9-蓝桥杯-历届试题-大臣的旅费-java
  3. 做爱做的事,做有快感的事
  4. 转载:如何在 SQL Server 中使用配置选项调整内存使用量
  5. SiameseRPN++分析
  6. 【UVA - 10037】Bridge(过河问题,经典贪心)
  7. mybatis generator修改默认生成的sql模板
  8. 【转】eclipse 自动关闭总结
  9. 聚类总结(中)——密度聚类
  10. 零基础怎么学习单片机?
  11. 实现一个计算体脂率的程序
  12. 如何做蛋白质互作网络图
  13. Qt交叉编译移植arm开发板
  14. 关于ip、pv、uv的概念
  15. 百钱买百鸡问题,Python编程解决
  16. 【论文写作】——设置中英文字体
  17. 【PC自动化测试-11】窗口控件的类型分类
  18. 《简约至上:交互式设计四策略》读书笔记
  19. archlinux安装nvidia驱动
  20. 齐鲁理工学院计算机专业在哪个校区,齐鲁理工学院有几个校区,哪个校区最好及各校区介绍...

热门文章

  1. 万能钥匙连上wifi如何看密码
  2. 高通qcom sdm450/msm8953平台 fingerprint指纹模组移植
  3. python 企业微信机器人自动推送文字和文件
  4. OpenGL蓝宝书源码学习(二十三)第七章——MultiTexture多重纹理
  5. MAC 系统安装 Go 语言:从零开始搭建 Go 开发环境
  6. [数据可视化] 词云(Word Cloud)
  7. erdas9.2服务启动失败,lmtools报错“lmgrd is not running: Cannot connect to license server”
  8. iOS开发--UIWebView
  9. kde钱包管理应用_5个出色的KDE应用可帮助您学习
  10. Unity 摄像机(Camera) 缩放 移动 旋转。