python性能分析工具总结
性能分析工具的使用
cProfile
介绍
它是一种确定性的性能分析器,提供了一组API帮助开 发者收集Python程序运行的信息,更确切地说,是统计每个函数消耗的 CPU时间。同时它还提供了其他细节,比如函数被调用的次数。 cProfile只测量CPU时间,并不关心内存消耗和其他与内存相关的信 息统计。尽管如此,它是代码优化过程中一个很不错的起点,因为大多 数时候,这个分析工具都会快速地为我们提供一组优化方案。 cProfile不需要安装,因为它是语言自带的一部分。要使用它,直接 导入cProfile包即可。
例子
简单调用
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()
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")
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的性
能分析信息
运行结果如下(为以上简单调用的结果)
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)显示了被分析函数所在的文件名、 行号、函数名。
cProfile工具的局限
不存在透明的性能分析器。也就是说,即使像cProfile这样消耗极小 的性能分析器,仍然会对代码实际的性能造成影响。当一个事件被触发 时,事件实际发生的时间相比性能分析器查询到的系统内部时钟的时间,还是会有一些延迟。另外,当程序计数器离开性能分析器代码,回 到用户代码中继续执行时,程序也会出现滞后。 除了这些之外,作为计算机内部的任何一段代码,内部时钟都有一个精 度范围,任何小于这个精度的测量值都会被忽略。也就是说,如果进行 性能分析的代码含有许多递归调用,或者一个函数需要调用许多函数, 开发者就应该对这个函数做特殊处理,因为测量误差不断地累计最终会 变得非常显著。
Line Profiler
不为python sdk自带,需要手动下载
pip install line_profiler
我们通过cProfile定位到了具体的耗时函数,下面就需要具体定位瓶颈出在哪行代码,这个时候就到了Line Profiler出场的时候了。与cProfile相比,Line Profiler的结果更加直观,它可以告诉你一个函数中每一行的耗时。
用法
定义一个测试文件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性能分析工具总结相关推荐
- python性能分析工具模块_python——关于Python Profilers性能分析器
1. 介绍性能分析器 profiler是一个程序,用来描述运行时的程序性能,并且从不同方面提供统计数据加以表述.Python中含有3个模块提供这样的功能,分别是cProfile, profile和ps ...
- python性能分析工具
1)cProfile cProfile可以嵌入到python代码中执行,比如: import cProfile cProfile.run('foo()', 'foo.out') 查看结果需要pstat ...
- cProfile——Python性能分析工具
Python自带了几个性能分析的模块:profile.cProfile和hotshot,使用方法基本都差不多,无非模块是纯Python还是用C写的.本文介绍cProfile. 例子 import t ...
- python性能分析工具_Python Profilers 分析器
实时用户手册¶ 本节是为 "不想阅读手册" 的用户提供的.它提供了非常简短的概述,并允许用户快速对现有应用程序执行评测. 要分析采用单个参数的函数,可以执行以下操作: import ...
- python性能分析工具模块_Python Profilers 分析器
实时用户手册¶ 本节是为 "不想阅读手册" 的用户提供的.它提供了非常简短的概述,并允许用户快速对现有应用程序执行评测. 要分析采用单个参数的函数,可以执行以下操作: import ...
- python输出程序运行时间_叨叨 Python 性能优化工具
虽然Python是一个"慢慢的"语言,但是不代表我们对性能没有任何的追求,在程序运行过程中,如果发现程序运行时间太长或者内存占用过大,免不了需要对程序的执行过程进行一些监测,找到有 ...
- 系统级性能分析工具perf的介绍与使用
测试环境:Ubuntu16.04 + Kernel:4.4.0-31 apt-get install linux-source cd /usr/src/tools/perf make &&am ...
- Linux常用性能分析工具汇总
文章目录 性能分析工具 top pstree mpstat vmstat pidstat perf proc tcpdump bcc工具箱 cachestat cachetop memleak fil ...
- Python 性能剖分工具
Python 性能剖分工具 眼看着项目即将完成,却被测试人员告知没有通过性能测试,这种情况在开发中屡见不鲜.接下来的工作就是加班加点地找出性能瓶颈,然后进行优化,再进行性能测试,如此这般周而复始直到通 ...
最新文章
- hadoop2 作业执行过程之作业提交
- java 大臣的旅费_PREV-9-蓝桥杯-历届试题-大臣的旅费-java
- 做爱做的事,做有快感的事
- 转载:如何在 SQL Server 中使用配置选项调整内存使用量
- SiameseRPN++分析
- 【UVA - 10037】Bridge(过河问题,经典贪心)
- mybatis generator修改默认生成的sql模板
- 【转】eclipse 自动关闭总结
- 聚类总结(中)——密度聚类
- 零基础怎么学习单片机?
- 实现一个计算体脂率的程序
- 如何做蛋白质互作网络图
- Qt交叉编译移植arm开发板
- 关于ip、pv、uv的概念
- 百钱买百鸡问题,Python编程解决
- 【论文写作】——设置中英文字体
- 【PC自动化测试-11】窗口控件的类型分类
- 《简约至上:交互式设计四策略》读书笔记
- archlinux安装nvidia驱动
- 齐鲁理工学院计算机专业在哪个校区,齐鲁理工学院有几个校区,哪个校区最好及各校区介绍...
热门文章
- 万能钥匙连上wifi如何看密码
- 高通qcom sdm450/msm8953平台 fingerprint指纹模组移植
- python 企业微信机器人自动推送文字和文件
- OpenGL蓝宝书源码学习(二十三)第七章——MultiTexture多重纹理
- MAC 系统安装 Go 语言:从零开始搭建 Go 开发环境
- [数据可视化] 词云(Word Cloud)
- erdas9.2服务启动失败,lmtools报错“lmgrd is not running: Cannot connect to license server”
- iOS开发--UIWebView
- kde钱包管理应用_5个出色的KDE应用可帮助您学习
- Unity 摄像机(Camera) 缩放 移动 旋转。