python 性能分析利器 py-spy
前言
当我们写完一个程序,对程序运行调试的时候,往往会遇到一些问题,有时候是程序运行过慢,有时候是程序启动后占用内存过高,还有比较少见的一种情况是内存一直增加,产生内存泄漏问题。那么其实我们要解决的问题可以从三个方向出发。1. 为什么程序这么慢?2. 为什么程序占用内存这么高?3.遇到内存泄露问题如何定位?
对于上述问题,可以查找到很多对应的工具,官方的profile,memory_profiler库,objgraph库,graphviz工具第三方的工具等等,但是很多工具都有特定的使用场景,或者工具本身就对程序有侵入性,会影响最终的结果。在这里我主要介绍自己踩坑使用下来能够去通用性的解决上述问题的两个工具:用于调查内存泄漏问题的官方包tracemalloc 和 无侵入式的用来调查程序性能瓶颈的开源库 py-spy。
1.解决 "为什么程序这么慢?"
诸如此类的问题,首先可以查看 python 程序的 cpu 使用率,如果在使用单核单进程模式下,cpu 利用率低下,无法达到100% 左右,可以优先从程序逻辑中查找是否是有磁盘或者网络io 占用资源过多,排查掉io 的问题后在从代码方面出发。
一. 使用py-spy 生成火焰图
repo 地址 https://github.com/benfred/py-spy 。
py-spy是一个非常好用而且简单的库,看完他的readme 介绍文档基本就可以入手使用spy。这个工具一是可以生成profile 火焰图,二是可以定位到程序中最耗时间的代码的位置。它的优点在于完全不用修改代码,相比较其他的一些性能调查工具,py-spy这一点非常棒,当你debug 一个线上正在运行的程序的时候,只需要提供进程id,py-spy 就可以直接生成火焰图。
sudo py-spy record -o profile.svg --pid 12345
"12345" 为程序运行的pid,当运行这行命令的时候,py-spy 开始抽样的程序simlple 并且生成火焰图,我们可以等待1分钟左右 ctrl+c 结束,这时候会在运行这行命令的当前目录下生成 profile.svg 火焰图, 如下图:
火焰图的分析非常简单直观,主要是看"平顶",看图中最下方那个峰顶是平的,那么程序的性能问题就可以从这里入手去解决,这里不详细介绍火焰图看法,不明白的同学可以自行百度。
通过生成火焰图分析程序瓶颈大概率可以找到并解决80%的程序性能问题,但是还有一种问题,如果我的火焰图没有平顶,但是程序依旧很慢,该如何定位问题?
二. 没有平顶情况下,定位程序中耗时最多函数/代码
如下图,通过火焰图并没有发现程序中的平顶
这时候要用到py-spy 提供的 top 命令
sudo py-spy top --pid 12345
输入上述命令后,在控制台会显示程序实时的运行状态,这里可以介绍一下图中4个参数的含义, 然后可以通过按1,2,3,4 四个按键,让程序按照下图所述排序。
1按%Own排序(当前在该函数中花费的时间的百分比) 2按%Total排序(函数及其子级中当前的时间百分比) 3按OwnTime排序(函数中花费的总时间) 4按TotalTime排序(该函数及其子项花费的总时间)
比较直观的 使用3 , 可以比较直接的看出程序运行中,所占比消耗时间最多的函数,然后从函数如图进行分析,如下图,可以看出 是wrap 装饰器函数消耗的时间最长,我们用wrapt 这个c写的装饰器进行替换后效率有了明显的提升。
总结 : 使用py-spy 相对于其他一些python性能分析工具,优势在于使用非常简单,而且无须对代码做任何改动,并且可以在保护现场情况下,直接生成火焰图,还可查看实时程序运行状态。
2. 解决 "为什么内存这么高"
内存泄漏问题其实在日常工作中比价难以遇见,我们现在使用 python ,golang ,java 大多数高级语言都已经自带了一套完成的垃圾回收机制(gc),所以一般遇见内存无限增加(内存泄漏)问题,比较难以debug。在这里主要使用的是 python 的官方提供的工具包 tracemalloc 。中文文档:https://docs.python.org/zh-cn/3.7/library/tracemalloc.html
通过阅读文档,文档中包的用法已经写的非常的详细了,这里我们主要通过获取两个程序运行时候的快照照片就行比对,看哪里内存进行了增长。
import tracemalloc tracemalloc.start() # ... start your application ... # 进行一次内存快照片 snapshot1 = tracemalloc.take_snapshot() # 在两次快照之间跑你的程序 snapshot2 = tracemalloc.take_snapshot() top_stats = snapshot2.compare_to(snapshot1, 'lineno') print("[ Top 10 differences ]") for stat in top_stats[:10]: # 打印出来内存增加最多的前十个代码地址。 print(stat)
通过内存的比对,可以准确定位到内存泄漏的地址。详细的还请看官方文档,但是目前个人感觉tracemalloc 包定位内存泄漏问题是最简洁直接的。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
设备接入IoT实例化节点开发实战
IoT物联网平台-实例化开发实战 前言 物联网平台支持用户创建实例,用于设备接入和业务管理。实例化有一下优势: 根据业务规模,购买匹配的实例规格,可大幅节约您的成本。 实例规格按需选择,避免触达公共实例默认限流阈值的问题。 实例间隔离更彻底,具有更高的数据可靠性和安全性 一个实例出现问题不会影响到您的其它实例 接下来,我们介绍一下实例化中完整的设备开发过程。 实例管理 进入IoT物联网平台控制台,选择实例管理,在实例列表的Tab,点击创建实例。 购买实例 根据业务规模,购买匹配的实例规格。 实例规格信息 购买成功后,我们在实例管理页面,能直接查看到实例规格信息。 切换实例 在实例管理页面,切换到实例设置Tab,在下拉框中选择目标实例,点击切换实例。刷新后,控制台就进入当前实例状态,顶部文案也会标记为实例。 查看实例节点信息 当我们选择实例后,设备MQTT接入点,云
- 下一篇
三种方式实现 Python 中的集合的交、并、补运算
三种方式实现 Python 中的集合的交、并、补运算 一 背景 集合这个概念在我们高中阶段就有所了解,毕业已多年,我们一起回顾一下几个集合相关的基本概念吧? 集合是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成集合的这些对象则称为该集合的元素。 集合具有以下几种性质: 确定性给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一,不允许有模棱两可的情况出现。 互异性一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。有时需要对同一元素出现多次的情形进行刻画,可以使用多重集,其中的元素允许出现多次。 无序性一个集合中,每个元素的地位都是相同的,元素之间是无序的。集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序。但就集合本身的特性而言,元素之间没有必然的序。 交集定义:由属于A且属于B的相同元素组成的集合,记作A∩B(或B∩A),读作“A交B”(或“B交A”),即A∩B={x|x∈A,且x∈B}, 如右图所示。注意交集越交越少。若A包含B,则A∩B=B,A∪B=A。 并集定义:由所有属于集合A或属于集合B的元素所组成的集合...
相关文章
文章评论
共有0条评论来说两句吧...