欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解如何分析python程序cpu占用率問題

 更新時間:2025年06月02日 08:25:20   作者:小郎碎碎念  
如果程序處理的數(shù)據(jù)比較多、比較復(fù)雜,那么在程序運行的時候,會占用大量的內(nèi)存,當(dāng)內(nèi)存占用到達(dá)一定的數(shù)值,程序就有可能被操作系統(tǒng)終止,這篇文章主要介紹了python程序cpu占用率問題的相關(guān)資料,需要的朋友可以參考下

問題

某天我看了一下目前在跑的一個 python 程序的 CPU 利用率,發(fā)現(xiàn)跑到了 100%

這個程序本身執(zhí)行的任務(wù)很多,開啟了很多的線程,跑到 100% 也可以說的過去。但是作為程序員的好奇心就被勾引起來了

java 可以通過 “top -Hp + jstack + 代碼” 精準(zhǔn)定位到 cpu 高利用率的原因。那么在 python 中,通過什么方式可以實現(xiàn)相同的目的?

一、方案調(diào)研

經(jīng)過簡單的調(diào)研,python 和 java 排查的思路不太一樣,有下面三種工具可以使用:cProfile、py-spy、yappi

cProfilepy-spyyappi
使用便捷性無需安裝,使用簡單(直接導(dǎo)入標(biāo)準(zhǔn)庫)需額外安裝,命令行操作,可能需要管理員權(quán)限需額外安裝,需在代碼中初始化/啟動/停止
功能特性統(tǒng)計函數(shù)調(diào)用次數(shù)、執(zhí)行時間等基礎(chǔ)信息,不支持多線程/異步代碼支持多線程/異步分析,可實時分析運行中的進(jìn)程,生成火焰圖支持多線程/協(xié)程分析,可自定義時鐘類型(CPU時間或墻鐘時間)
性能影響對性能有一定影響(大型程序更明顯)非侵入式,對目標(biāo)進(jìn)程性能影響極小性能影響較小,但大規(guī)模程序可能有開銷
分析結(jié)果展示文本輸出(復(fù)雜場景難解讀)火焰圖可視化,直觀定位瓶頸詳細(xì)統(tǒng)計數(shù)據(jù)(需學(xué)習(xí)成本),支持多種輸出格式
適用場景開發(fā)階段初步性能分析(定位耗時函數(shù))生產(chǎn)環(huán)境實時分析(快速定位CPU高負(fù)載問題)復(fù)雜多線程/異步程序深度分析

二、每個工具實際使用的效果

2.1 cProfile

基本使用方式是修改程序的啟動入口

import cProfile

def main():
    ...  # 程序入口

if __name__ == "__main__":
    cProfile.run('main()', sort='cumtime')

然后控制臺啟動程序,運行足夠長的時間后,終止程序(ctrl + c)時就會輸出對應(yīng)的函數(shù)執(zhí)行統(tǒng)計,如下圖:

輸出表格各列含義如下

  • ncalls:函數(shù)被調(diào)用的次數(shù)。
  • tottime:函數(shù)自身執(zhí)行所耗費的總時間,不包含調(diào)用其他函數(shù)的時間。
  • percall:每次調(diào)用函數(shù)所花費的平均時間,即 tottime / ncalls。
  • cumtime:函數(shù)執(zhí)行的累積時間,包含調(diào)用其他函數(shù)的時間。
  • percall:函數(shù)每次調(diào)用的累積平均時間,即 cumtime / ncalls。
  • filename:lineno(function):函數(shù)所在的文件名、行號以及函數(shù)名。

整體上看相對來說不夠直觀,所以我的策略是,將結(jié)果拷貝進(jìn) excel 表格,分別按照 tottime 和 cumtime 排序,然后將結(jié)果取前 20 行放入大語言模型進(jìn)行分析。提示詞如下

你是一位資深的 python 專家和系統(tǒng)運維專家

你的任務(wù)是使用 cProfile 工具排查 python 程序 cpu 利用率過高的問題,并提出對應(yīng)的優(yōu)化和解決建議

cProfile 的輸出如下

按照 cumtime 排序

---
ncalls	tottime  pe	rcall	cumtime  pe	rcall	filename:lineno(function)
91313	0.516	0	56.86	0.001	conv.py:53(forward_fuse)
27123	0.685	0	35.403	0.001	block.py:346(forward)
10435/10350	0.086	0	34.571	0	03 {method 'extend' of 'list' objects}
19892	0.086	0	32.822	0.002	block.py:239(<genexpr>)
97642	0.352	0	32.226	0	conv.py:553(forward)
3616	0.097	0	26.887	0.007	block.py:236(forward)
97642	0.259	0	21.341	0	conv.py:536(_conv_forward)
3616	0.106	0	20.761	0.006	block.py:459(forward)
97642	10.577	0	17.058	0	{built-in method torch.conv2d}
2372/2090	0.051	0	15.157	0.007	pooled_db.py:360(cache)
3671/3328	0.115	0	14.816	0.004	serving.py:259(write)
14464	0.043	0	13.573	0.001	block.py:462(<genexpr>)
7713/7070	0.046	0	12.46	0.002	socket.py:500(close)
3669/2219	0.1	0	12.376	0.006	selectors.py:451(select)
3798/3596	0.047	0	12.338	0.003	connections.py:431(_force_close)
2372/2091	0.042	0	12.221	0.006	steady_db.py:326(_reset)
904	0.107	0	11.436	0.013	head.py:317(forward)
87697	0.187	0	11.194	0	activation.py:431(forward)
2372/2091	0.024	0	9.936	0.005	steady_db.py:436(rollback)
3671/3585	0.056	0	9.425	0.003	server.py:493(send_response)
...(省略)
---

按照 tottime 如下

---
ncalls	tottime  pe	rcall	cumtime  pe	rcall	filename:lineno(function)
97642	10.577	0	17.058	0	{built-in method torch.conv2d}
1492/89	10.445	0.007	0.751	0.008	{method 'read' of 'cv2.VideoCapture' objects}
3660/317	9.277	0.003	2.85	0.009	{method 'poll' of 'select.poll' objects}
741/0	6.681	0.009	0		{built-in method time.sleep}
3669/2219	3.78	0.001	7.225	0.003	{method 'poll' of 'select.epoll' objects}
1522/185	2.446	0.002	0.4	0.002	{flip}
6328	1.948	0	3.359	0.001	{built-in method torch.einsum}
345354/12	1.613	0	1.368	0.114	module.py:1740(_call_impl)
903/1	1.581	0.002	0.003	0.003	{resize}
87697	1.573	0	4.584	0	{built-in method torch._C._nn.silu_}
16399/10112	1.556	0	3.308	0	00 {method 'recv_into' of '_socket.socket' objects}
23770/139	1.544	0	0.19	0.001	{method 'sendall' of '_socket.socket' objects}
345353/12	1.184	0	1.368	0.114	module.py:1732(_wrapped_call_impl)
3616	1.117	0	1.921	0.001	{built-in method torch._C._nn.linear}
439192	1.1	0	1.347	0	client.py:75(__setitem__)
9352/153	1.043	0	0.016	0	{method 'recv' of '_socket.socket' objects}
53147/50648	0.798	0	2.345	0	00 {method 'settimeout' of '_socket.socket' objects}
516359	0.783	0	0.783	0	module.py:1918(__getattr__)
18081	0.722	0	1.736	0	{built-in method torch.cat}
3616	0.689	0	7.775	0.002	block.py:426(forward)
...(省略)
---

你的分析結(jié)果應(yīng)該包含每個問題的影響程度,以及你分析的依據(jù)(即從 cProfile 哪個數(shù)據(jù)得到的這個結(jié)論)

具體輸出就不展示了,大語言模型每次都可以分析出問題的原因之一在于模型推理存在大量的 cpu 使用(未經(jīng)過工程上的優(yōu)化)。但是一些其他問題,輸出不太穩(wěn)定,例如 connections.py:431(_force_close) 有時能捕捉到是因為頻繁地關(guān)閉數(shù)據(jù)庫連接問題,但很多時候有捕捉不到這個問題。

主要的問題在于,cProfile 輸出的每一行,都是以函數(shù)為維度的,函數(shù)之間的關(guān)聯(lián)無法很好的捕捉到,例如 method 'extend' of 'list' objects 有點模糊。

2.2 py-spy

安裝

pip install py-spy

使用(需要 sudo 權(quán)限),輸出火焰圖

# -o 輸出文件名
# -d 采樣多久
py-spy record -o profile.svg -d 60 -p <pid>

svg 文件可以直接使用瀏覽器打開,從下圖可以看出,火焰圖可以很直觀地定位到一些瓶頸問題

不過遺憾的是,svg 文件大語言模型還無法很好的支持,如果截圖給 AI 分析,又有很多文字省略了。

2.3 yappi

安裝

pip install yappi

使用,對于有執(zhí)行結(jié)束的程序

yappi.set_clock_type("cpu")  # cpu or wall
yappi.start()

my_function()

yappi.stop()
stats = yappi.get_func_stats()
# stats.print_all()
stats.save('yappi_stats.callgrind', type='callgrind')

對于無法結(jié)束的程序,例如網(wǎng)絡(luò)接口,可以如下(使用 kill -10 <pid> 來觸發(fā)寫入結(jié)果,實測寫的過程會 hang 住主進(jìn)程,最好再單獨開啟一個寫文件線程!

import yappi
import signal

def start_all():
    app.run(debug=False, host='0.0.0.0', port=5000)


def handle_sigusr1(signum, frame):
    print("接收到 SIGUSR1 信號,正在停止 yappi 分析并保存結(jié)果...")
    yappi.stop()
    stats = yappi.get_func_stats()
    stats.save('yappi_stats.callgrind', type='callgrind')
    print("yappi 分析結(jié)果已保存為 yappi_stats.callgrind")

if __name__ == '__main__':
    yappi.set_clock_type("cpu")
    yappi.start()

    # 注冊 SIGUSR1 信號處理函數(shù)
    signal.signal(signal.SIGUSR1, handle_sigusr1)

    start_all()

callgrind 是一種特殊格式的文件,windows 可以使用 QCacheGrind 查看(下載地址

我仔細(xì)看了這里列的熱點代碼,整體上來說和 cProfile 以及 py-spy 相差較多,基本分析不出來什么有用信息。

我也不知道是不是自己使用姿勢有問題,但如果這個工具學(xué)習(xí)成本這么高,說明它實際上也不是一個簡單易用的工具。

總結(jié)

整體使用下來,cProfile 和 py-spy 的結(jié)果是可以相互印證的。但我還是 比較推薦 py-spy,原因如下

  • 安裝簡單
  • 不需要改代碼,使用方便
  • 火焰圖結(jié)果看起來直觀易懂
  • 看文檔還可以把完整的執(zhí)行棧 dump 出來,這樣實際可以和 java 的排查思路一致了(上面例子中沒有嘗試,后面有實際問題可以嘗試一下)

當(dāng)然,實際情況下,很可能要多種工具齊上陣,綜合分析才能得到準(zhǔn)確的問題排查結(jié)果。

到此這篇關(guān)于python程序cpu占用率問題的文章就介紹到這了,更多相關(guān)python程序cpu占用率問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python中tensorflow的argmax()函數(shù)的使用小結(jié)

    Python中tensorflow的argmax()函數(shù)的使用小結(jié)

    在TensorFlow中,argmax()函數(shù)是一個非常重要的操作,它用于返回給定張量(Tensor)沿指定軸的最大值的索引,下面就來介紹一下argmax()的使用,感興趣的可以了解一下
    2025-05-05
  • 解析pandas apply() 函數(shù)用法(推薦)

    解析pandas apply() 函數(shù)用法(推薦)

    這篇文章主要介紹了pandas apply() 函數(shù)用法,大家需要掌握函數(shù)作為一個對象,能作為參數(shù)傳遞給其它函數(shù),也能作為函數(shù)的返回值,具體內(nèi)容詳情跟隨小編一起看看吧
    2021-10-10
  • 最新評論