python 性能分析利器 py-spy

简介: python 内存泄漏工具以及性能瓶颈分析工具分享。

前言

​ 当我们写完一个程序,对程序运行调试的时候,往往会遇到一些问题,有时候是程序运行过慢,有时候是程序启动后占用内存过高,还有比较少见的一种情况是内存一直增加,产生内存泄漏问题。那么其实我们要解决的问题可以从三个方向出发。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 火焰图, 如下图:

undefined

 火焰图的分析非常简单直观,主要是看"平顶",看图中最下方那个峰顶是平的,那么程序的性能问题就可以从这里入手去解决,这里不详细介绍火焰图看法,不明白的同学可以自行百度。

​ 通过生成火焰图分析程序瓶颈大概率可以找到并解决80%的程序性能问题,但是还有一种问题,如果我的火焰图没有平顶,但是程序依旧很慢,该如何定位问题?

二. 没有平顶情况下,定位程序中耗时最多函数/代码

如下图,通过火焰图并没有发现程序中的平顶
undefined

​ 这时候要用到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写的装饰器进行替换后效率有了明显的提升。

undefined

总结 : 使用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 包定位内存泄漏问题是最简洁直接的。

目录
相关文章
|
6天前
|
测试技术 持续交付 Apache
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
【9月更文挑战第10天】随着软件应用的不断扩展,性能测试成为确保系统稳定运行的关键环节。本文通过对比Apache JMeter和Locust,探讨了如何在Python环境中利用这两款工具挖掘更多性能测试潜力。JMeter是一款成熟且功能强大的开源工具,支持多种协议,适用于各种应用的测试;而Locust则基于Python,通过简单脚本模拟HTTP请求,更适合Web应用测试。
14 2
|
1月前
|
测试技术 持续交付 Apache
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
【8月更文挑战第5天】随着软件应用的扩展,性能测试至关重要。Apache JMeter是一款成熟且功能强大的开源工具,支持多种协议,可通过命令行模式执行复杂测试计划,并能与Python集成实现自动化。Locust则是一个基于Python的负载测试工具,通过简单脚本模拟HTTP请求,特别适合Web应用测试,支持自定义请求和深度集成Python库。两者各有优势:JMeter适用于多种应用测试,有直观图形界面;Locust专注HTTP请求,对熟悉Python的开发者更为灵活。结合Python的强大功能,这些工具能帮助我们深入挖掘性能测试潜力,提高应用的稳定性和可靠性。
77 3
|
2月前
|
数据可视化 PyTorch Serverless
Python 性能分析的几个方法,找到你代码中的那个她
我们在编写了一个脚本在笔记本上处理一些数据,然后去喝杯咖啡或者上了个厕所,15分钟后回来时发现进度才完成不到10%。 我们的脑袋里面就会发问:为什么这么慢?究竟是在哪个部分是慢的?是读取数据、处理数据还是保存数据?如何让它变快?它真的很慢吗? 有了这个疑问我们尝试去解决这个问题,下面我们介绍几个 python 性能分析的工具。
3个常用的Python性能分析工具及其使用方法
以下是几个常用的性能分析工具及其使用方法和常用命令:
|
4月前
|
缓存 数据可视化 数据库
基于Python的性能分析
性能分析就是对程序的性能进行分析,从用户角度出发就是运行的速度,占用的内存。 通过对以上情况的分析,来决定程序的哪部份能被优化。提高程序的速度以及内存的使用效率。 首先我们要弄清楚造成时间方面性能低的原因有哪些 1. 沉重的I/O操作,比如读取分析大文件,长时间执行数据库查询,调用外部服务例如请求。 2. 出现了内存泄露,消耗了所有内存,导致没有内存使用程序崩溃。 3. 未经过优化的代码被频繁执行。 4. 密集的操作在可以缓存的时没有缓存,占用大量资源。
|
10月前
|
Python
170 python - 内置类型性能分析
170 python - 内置类型性能分析
34 0
|
数据可视化 中间件 API
Python性能分析利器pyinstrument讲解
Python性能分析利器pyinstrument讲解
541 0
Python性能分析利器pyinstrument讲解
|
算法 Python
python散列表实现查找,使用了多种算法并测试对比进行了性能分析(查找效率)
散列表实现查找 本章是填补之前文章的坑,对哈希算法进行了实现,使用了平方取中法/除留余数法进行哈希映射,使用开放地址与公共溢出区解决冲突,同时对不同方法进行了性能分析对比,最后进行了总结。 可以转载,但请声明源链接:文章源链接justin3go.com(有些latex公式某些平台不能渲染可查看这个网站)
115 0
|
Python
Python日学壹技:性能分析
Python日学壹技:性能分析
73 0
|
Python 数据采集
Python---Requests库的爬取性能分析
“任意”找个url,测试一下成功爬取100次网页的时间。(某些网站对于连续爬取页面将采取屏蔽IP的策略,所以,要避开这类网站。) import requests import time def getHtmlText(url): try: # try except:用于异常处理 r = requests.
1262 0