
前言
之前有介绍关于性能分析上相关几个工具主要有:cProfile和line_profiler,这两个主要是针对耗时统计分析的,如果需要分析每一行代码所增减的内存占用情况的话,就需要祭出‘ memory_profiler’。memory_profiler 是一个第三方库的模块,它可以有效定位到我们没一行代码占用内存的情况。
环境:python 3.5
本节主要内容:
相关单位换算介绍 memory_profiler 安装 memory_profiler 简单例子的使用 memory_profiler 把分析结果写入文件 memory_profiler 可视化分析结果 memory_profiler 多进程情况下的分析 memory_profiler 结合Flask框架 memory_profiler设置内存断点
铺垫篇
1、存单位换算说明:

一、 b与B(位与字节)
bit 位,计算机中最小的数据单位。每一位的状态只能是0或1 。即 1b=1bit byte 字节,8个二进制位构成1个字节。由0或1合计8位任意搭配组成,每一位的状态只能是0或1 1 byte = 8 bit
1个英文字母或者数字占用1个byte的空间,1个汉字占据2个byte的空间。
二、K与Ki(千与千位二进制)
kb (kilobyte)千字节.
k(kilo) 是千的意思,比如1千米,1km;1千克.1kg.等等.加上后面的单位数据乘以1000即可.如工资3k.
1k=1kilo=
1kb=1000byte=8bit*1000(8千位)
Ki(Kibi,Kilo binary的缩写):千位二进制的意思。为了表示信息数据传输二进制而创设的1024,一般用Ki而不用ki.
Ki=1Kibi= = 1024
1KiB(Kibibyte)=1024B(byte) :KiB(Kibibyte)是千位二进制字节的意思
1Kib=1024*8b(bit) :Kib(Kibibit)就是千位二进制位的意思.
1Ki=1ki :一般都用Ki.1KiB=8Kib
三、M与Mi(兆与兆位二进制)
1M=1mega= :M(mega)就是兆的意思.这里和米(m)不是一个单位.
1MB=B :MB(megabyte)就是兆字节的意思.
1MB=B = (byte)=*8(bit)
1Mb=106*8b : Mb(megabit)就是兆位的意思.
1Mb=*8(bit)
1m=1M :规定用M.1MB=8Mb.电信说的100兆宽带就是100Mb,因此要转换为我们常说的网速单位需要除以8,即100Mb=100/8=12.5MB。
1Mi=1Mibi= :Mi(mebi,即Mega binary的缩写)就是兆位二进制的意思.
1MiB=B :MiB(mebibyte)就是兆位二进制字节的意思.
1Mib=220b :Mib(mebibit)就是兆位二进制位的意思.
1mi=1Mi :规定用Mi.1MiB=8Mib.
四、G与Gi(吉与吉位二进制)
1G=1giga=109 G(giga) :就是吉的意思.这里和克(g)不是一个单位. 1GB=109B GB(gigabyte): 就是吉字节的意思. 1Gb=109*8b Gb(gigabit): 就是吉位的意思. 1g=1G :规定用G. 1GB=8Gb. 1 千兆字节(g)=1024 兆字节(MB)

1Gi=1gibi=109 : Gi(gibi,Giga binary的缩写)就是吉位二进制的意思. 1GB=109B :GiB(gibibyte)就是吉位二进制字节的意思. 1Gb=109*8b :Gib(gibibit)就是吉位二进制位的意思. 1gi=1Gi :规定用Gi. 1GiB=8Gib.
2、其他单位说明
1、 MiB、KiB、Mb、kb
1kb=1024B 或者1KB=1000B
1KiB=1024B
1Mb=1024KB
1MiB=1024KB
2、Bps、bps:
Bps Byte per second,字节/秒. bps bit per second 位/秒 如家用的2M宽带,也就是2M bps,换成Bps也就是(2M/8 )Bps = 256K Bps,所以我们用2M宽带下载时的最大下载速度就是256KB左右。我们平常所说的百兆、千兆网卡,其单位就是bps,而非Bps。
3、GT/s、GB/s:
常用来描述计算机内部总线的传输速度 GT/s:giga transfers per second,可以通俗的理解为“G次/秒”,表示传输速度。如Core i7 QPI总线传输速度可以达到4.8GT/s,HT 3.0总线可以达到5.2GT/s,也就是 说QPI总线每秒可以传输4.8G次,HT 3.0总线则是每秒5.2G次。
4、GB/s
即G Byte/s,如QPI总线的带宽是25.6GB/s,即每秒能传输25.6G字节。两者间的换算:比如1333MHz的FSB,每秒传输1333M(1.333G)次,每次传输64 bit数据,也就是8 Byte,所以它的传输带宽就是1.333*8=10.7GB/S。
实践篇
官网地址
https://pypi.org/project/memory-profiler/
安装
(.venv) PS D:\code\vscode\py> pip install memory_profiler
PS:安装psutil可加速memory_profile(windos环境下建议安装psutil)
pip install psutil
1.最简示例
Filename: d:/code/vscode/py/memory_profiler_oy.py
Line # Mem usage Increment Occurences Line Contents
============================================================
13 30.5 MiB 30.5 MiB 1 @profile
14 def ceshi():
15 30.5 MiB 0.0 MiB 1 a = [1, 1, 2, 2, "小钟同学"]
16 30.5 MiB 0.0 MiB 1 return a
(.venv) PS D:\code\vscode\py>
参数说明:
Mem usage:表示执行该行后Python解释器的内存使用情况 Increment:表示当前行的内存相对于上一行的差异,即自己本身增长了多少,如果减少了则不显示.
关于MIB:
1MB=B = (byte)=*8(bit)
1MiB = 1,024KiB(千字节(kb)) =Byte=1048576 Byte=1.1757813兆字节(mb)
1 千字节kb=0.0009766 兆字节mb 1MiB = 1,024KiB=1,024kb=1024*0.0009766 兆字节mb= 1.0000384MB
分析占用情况:说明30.5MiB=30.5 *0.0009766兆字节(mb)=0.0297863M
2.官网示例
Filename: d:/code/vscode/py/memory_profiler_oy.py
Line # Mem usage Increment Occurences Line Contents
============================================================
13 30.4 MiB 30.4 MiB 1 @profile
14 def my_func():
15 34.3 MiB 3.8 MiB 1 a = [1] * (10 ** 6)
16 110.6 MiB 76.3 MiB 1 b = [2] * (2 * 10 ** 7)
17 34.3 MiB -76.3 MiB 1 del b
18 34.3 MiB 0.0 MiB 1 return a
(.venv) PS D:\code\vscode\py>
参数说明:
line 已分析的代码的行号 Mem usage:表示执行该行后Python解释器的内存使用情况 Increment:表示当前行的内存相对于上一行的差异,即自己本身增长了多少,如果减少了则不显示.
3.分析结果写入本地
from memory_profiler import profile
@profile(precision=4, stream=open('memory_profiler.log', 'w+'))
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
sdsdk()
return a
@profile(precision=4, stream=open('memory_profiler2.log', 'w+'))
def sdsdk():
sasd = []
for i in range(2000):
sasd.append(1)
if __name__ == '__main__':
my_func()
最终文件生成:

4.以图片的形式展示出来分析结果
4.1 生产文件信息
mprof run test.py
错误:
(.venv) PS D:\code\vscode\py> mprof run .\memory_profiler_oy.py
mprof: Sampling memory every 0.1s
running new process
running as a Python program...
Traceback (most recent call last):
File "C:\Users\mayn\AppData\Local\Programs\Python\Python35-32\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "C:\Users\mayn\AppData\Local\Programs\Python\Python35-32\lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "d:\code\vscode\py\.venv\lib\site-packages\memory_profiler.py", line 1303, in <module>
exec_with_profiler(script_filename, prof, args.backend, script_args)
File "d:\code\vscode\py\.venv\lib\site-packages\memory_profiler.py", line 1204, in exec_with_profiler
exec(compile(f.read(), filename, 'exec'), ns, ns)
UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 23: illegal multibyte sequence
(.venv) PS D:\code\vscode\py>
网上解决方案:
> gbk解码错误,解决方案:
- 首先进入 memory_profiler.py文件中,
- 找到第with open关键词, 把with open(filename) as f: 更改成 with open(filename, encoding=’utf-8’) as f:

再执行命令:
mprof run .\memory_profiler_oy.py
生成两个文件(因为两个装饰器):

4.2 记录了内存随时间的变化的图片可视化生成
(.venv) PS D:\code\vscode\py> mprof plot .\mprofile_20210308140851.dat
matplotlib is needed for plotting.
No module named 'pylab'
(.venv) PS D:\code\vscode\py>
解决错误:
pip install matplotlib
PS:需要依赖使用matplotlib来进行图片的绘制
然后继续执行生产图片的命令:
mprof plot .\mprofile_20210308140851.dat

mprof的可用命令是:
mprof run:运行可执行文件,记录内存使用情况(默认情况下仅跟踪父进程)
(.venv) PS D:\code\vscode\py> mprof run .\memory_profiler_oy.py
mprof: Sampling memory every 0.1s
running new process
running as a Python program...
mprof plot:绘制一个记录的内存使用情况(默认情况下,最后一个Using last profile data.)
(.venv) PS D:\code\vscode\py> mprof plot
Using last profile data.
mprof list:以一种用户友好的方式列出所有记录的内存使用情况文件。
(.venv) PS D:\code\vscode\py> mprof list
0 mprofile_20210308140851.dat 14:08:51 08/03/2021
1 mprofile_20210308141254.dat 14:12:54 08/03/2021
(.venv) PS D:\code\vscode\py>
mprof clean:删除所有记录的内存使用情况文件。 mprof rm:删除特定记录的内存使用情况文件
5、涉及多进程情况下分析结果
当使用mprof run:运行可执行文件,记录内存使用情况(默认情况下仅跟踪父进程)。如果我们的程序涉及到多进程的时候,就如果需要分别跟进每个进程的内存使用情况的话,就需要做额外的参数传递处理:目前我们的测试代码为:
from memory_profiler import profile
@profile(precision=4, stream=open('memory_profiler.log', 'w+'))
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
sdsdk()
return a
@profile(precision=4, stream=open('memory_profiler2.log', 'w+'))
def sdsdk():
sasd = []
for i in range(2000):
sasd.append(1)
if __name__ == '__main__':
my_func()
第一种机制:
把主进程和子进程的内存使用情况的报告都输出,使用include_children参数
执行:
mprof run --include-children xxxxxx.py
结果:
(.venv) PS D:\code\vscode\py> mprof run --include-children .\memory_profiler_oy.py
会生产三个文件,一个是日志文件,另一个是xxx.dat文件:

第二种机制:
分别的统计跟踪每个子进程 执行:
mprof run --multiprocess xxxxxx.py
6、memory_profiler 结合Flask框架
from memory_profiler import profile
from flask import Flask, jsonify
import time
app = Flask(__name__)
@app.route('/')
@profile(precision=4)
def line_test():
for item in range(5):
time.sleep(0.1)
return jsonify({'code': 200})
if __name__ == '__main__':
app.run()
输出结果:
Filename: d:/code/vscode/py/memory_profiler_oy.py
Line # Mem usage Increment Occurences Line Contents
============================================================
20 35.7969 MiB 35.7969 MiB 1 @app.route('/')
21 @profile(precision=4)
22 def line_test():
23 35.7969 MiB 0.0000 MiB 6 for item in range(5):
24 35.7969 MiB 0.0000 MiB 5 time.sleep(0.1)
25
26 35.7969 MiB 0.0000 MiB 1 return jsonify({'code': 200})
127.0.0.1 - - [08/Mar/2021 16:06:29] "GET / HTTP/1.1" 200 -
7、mprof plot参数补充说明:
mprof plot --flame(函数名称和时间戳名称将显示在悬停上).\mprofile_20210308140851.dat --flame(函数名称和时间戳名称将显示在悬停上) mprof plot -t'记录的内存使用情况 mprof plot -n 使用n标志隐藏函数时间戳 mprof plot -s 使用s标志绘制趋势线及其数值斜率 mprof plot -s >0 mprof plot -s ~0 mprof plot -s <0
pS:趋势线用于说明目的,并绘制为(非常)小的虚线。
> 0,可能意味着内存泄漏。
〜0(如果为0或接近0),则可以认为内存使用情况稳定。
<0将根据预期的进程内存使用模式来解释,也可能意味着采样周期太小。
8、memory_profiler设置内存断点
意思就是给我们的内存使用量设置一个阀值,当触发到这个阀值的时候,停止执行并运行到pdb调试器。使用此功能的前提是不需要再相关的函数加上@profile装饰功能。
然后使用命令启动程序:
$ python -m memory_profiler --pdb-mmem = 100 my_script.py
其中:--pdb-mmem = 100 中的100表示的就是内存阈值的数字,以MB为单位。当我们的函数消耗内存达到100 MB以上的内存,就进入pdb调试器。
参考资料:
官网参考 :https://pypi.org/project/memory-profiler/
更多示例对比参考 https://www.cnblogs.com/kaituorensheng/p/5669861.html#_label9
说明
部分的图或资料来互联网收集整理,如有侵权,烦请联系,我会立即进行删除。
End
纯属个人实践中相关经验之谈,如有纰漏,还希望大佬们多多提点!小钟同学 | 文 【原创】| QQ:308711822





