前言
最近使用ortools CP-SAT解決業(yè)務(wù)上的調(diào)度規(guī)劃問題時,求解過程中項目內(nèi)存占用急劇飆升,所以需要使用內(nèi)存監(jiān)控分析工具進行分析定位問題,自然而然的就找到了memory_profiler。
memory_profiler用于監(jiān)控進程的內(nèi)存消耗以及對Python程序的內(nèi)存消耗進行逐行分析。它是一個依賴于 psutil的純python模塊,支持基于代碼逐行及時間維度的分析監(jiān)控。
用法
memory_profiler支持代碼逐行分析和時間維度的分析,一共有三種用法。
首先安裝:
pip install -U memory_profiler
- 僅使用裝飾器
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
命令行攜帶額外參數(shù) -m memory_profiler執(zhí)行
python -m memory_profiler profiler.py
輸出結(jié)果:
輸出結(jié)果:
Line # Mem usage Increment Occurrences Line Contents
=============================================================
3 18.777 MiB 18.777 MiB 1 @profile
4 def my_func():
5 26.410 MiB 7.633 MiB 1 a = [1] * (10 ** 6)
6 179.000 MiB 152.590 MiB 1 b = [2] * (2 * 10 ** 7)
7 26.410 MiB -152.590 MiB 1 del b
8 26.410 MiB 0.000 MiB 1 return a
- 使用裝飾器+導(dǎo)包
#注意這里導(dǎo)入了profile
from memory_profiler import profile
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
按照此種方式命令行不需要攜帶額外參數(shù)直接執(zhí)行即可
python profiler.py
輸出結(jié)果:
Line # Mem usage Increment Occurrences Line Contents
=============================================================
4 17.9 MiB 17.9 MiB 1 @profile
5 def my_func():
6 25.6 MiB 7.6 MiB 1 a = [1] * (10 ** 6)
7 178.2 MiB 152.6 MiB 1 b = [2] * (2 * 10 ** 7)
8 25.6 MiB -152.6 MiB 1 del b
9 25.6 MiB 0.0 MiB 1 return a
-
使用mprof run執(zhí)行
使用 mprof 執(zhí)行程序在時間維度分析進程的內(nèi)存使用情況。下面介紹了一共有四種情況,分別是:單進程,多進程,記錄子進程內(nèi)存占用,多進程并記錄子進程內(nèi)存占用。
mprof run <executable>
mprof run --multiprocess <executable>
mprof run --include-children <executable>
mprof run --include-children --multiprocess <executable>
使用 mprof run執(zhí)行python文件后會生成一個.dat文件,再通過**mprof plot **+ .dat文件接口繪制圖片(需要先安裝matplotlib)例如:
mprof plot mprofile_20251024165215.dat

mprof plot命令還支持攜帶以下參數(shù):
-n 隱藏時間戳
mprof plot -n mprofile_20251023133554.dat
輸出如下:

-t 設(shè)置標題
mprof plot -t test_title mprofile_20251023133554.dat

-s 顯示增長斜率
mprof plot -s mprofile_20251023133554.dat

除上述方式外,還支持在代碼中使用hardcode的方式使用,memory_profiler提供如下方法:
def memory_usage(proc=-1, interval=.1, timeout=None, timestamps=False,
include_children=False, multiprocess=False, max_usage=False,
retval=False, stream=None, backend=None, max_iterations=None):
需要關(guān)注的是proc參數(shù):
proc參數(shù)可以是:整型,字符串,元組,Popen??梢杂杀硎綪ID的整數(shù)串、Popen對象或表示Python函數(shù)的元組給出。元組包含三個值(f, args, kw),并指定運行函數(shù)f(args, kw)。當(dāng)前進程設(shè)置為-1(默認值),例如如下調(diào)用方式:
from memory_profiler import memory_usage
def solve_task_progress_with_mem(employees, time_grids, plan_id,
enable_tracemalloc: bool | None = None,
interval: float = 0.01) -> float:
"""
以連續(xù)采樣的方式執(zhí)行 solve_task_progress,并返回/打印全過程內(nèi)存峰值(MiB)。
- interval: 采樣間隔秒
- include_children=True: 統(tǒng)計子進程(若有)
"""
mem_series = memory_usage(
(solve_task_progress, (employees, time_grids, plan_id),
{'enable_tracemalloc': enable_tracemalloc}),
interval=interval,
include_children=True,
multiprocess=True
)
peak = max(mem_series) if mem_series else 0.0
print(f"[mem-peak] peak_rss_mib={peak:.1f} samples={len(mem_series)} interval_s={interval}")
return peak
附上git鏈接:memory_profiler