TShopping

 找回密碼
 註冊
搜索
查看: 2047|回復: 0
打印 上一主題 下一主題

[分享] 如何監視Python程式的記憶體使用情況

[複製鏈接]
跳轉到指定樓層
1#
發表於 2021-8-18 10:43:27 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
 
Push to Facebook
我們使用Python和它的資料處理庫套件(如panda和scikiti]

1. 詢問作業系統
跟蹤記憶體使用情況的最簡單方法是使用作業系統本身。您可以使用top來提供您在一段時間內使用的資源的概述。或者,如果您想要現場檢查資源使用情況,您可以使用ps命令:
  1. $ ps -m -o %cpu,%mem,command  
  2. %CPU %MEM COMMAND  
  3. 23.4  7.2 python analyze_data.py  
  4. 0.0  0.0 bash
複製代碼

<span]m標誌指示ps按照程序使用最多記憶體的順序顯示結果。o標誌控制顯示每個程序的哪些屬性——在本例中是使用的CPU百分比、消耗的系統記憶體百分比和正在執行的程序的命令列。CPU百分比將一個完整的CPU核心計算為100%的使用率,因此如果您有一個4核的機器,可能會看到總計高達400%的CPU使用率。還有其他輸出選項用於顯示其他程序屬性,以及用於控制顯示哪些程序的ps的其他標誌。


結合一些創造性的shell指令碼,可以編寫一個監視指令碼,使用ps跟蹤任務的記憶體使用情況。

2. tracemalloc
Python直譯器的操作中有大量的hooks,可以在Python程式碼執行時用於監視和內省。pdb使用這些鉤子來提供除錯;覆蓋率也使用它們來提供測試覆蓋率。tracemalloc模組還使用它們來提供一個瞭解記憶體使用情況的視窗。

tracemalloc是在Python 3.4中新增的一個標準庫模組,它跟蹤Python直譯器分配的每個單獨的記憶體塊。tracemalloc能夠提供關於執行Python程序中記憶體分配的非常細粒度的資訊:
  1. import tracemalloc

  2. tracemalloc.start()
  3. my_complex_analysis_method()
  4. current, peak = tracemalloc.get_traced_memory()
  5. print(f"Current memory usage is {current / 10**6}MB; Peak was {peak / 10**6}MB")
  6. tracemalloc.stop()
複製代碼

呼叫tracemplugin]不過,這種程度的細節是要付出代價的。tracemalloc將自己深深地注入到正在執行的Python程序中——正如您所預期的那樣,這會帶來效能損失。在我們的測試中,我們觀察到在執行分析時使用tracemalloc的速度下降了30%。在分析單個程序時,這可能是可以的,但在生產中,您確實不希望僅僅為了監視記憶體使用情況而降低30%的效能。


3. 抽樣
幸運的是,Python標準庫提供了另一種觀察記憶體使用情況的方法—resource模組。resource模組為程式分配的資源提供基本控制,包括記憶體使用:
  1. import resource
  2. usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
複製代碼

getrusage()的呼叫返回程式所使用的資源。常量RUSAGE_SELF表示我們只對這個程序使用的資源感興趣,而不是它的子程序。返回的物件是一個結構,它包含一系列作業系統資源,包括CPU時間、訊號、上下文切換等;但就我們的目的而言,我們感興趣的是maxrss——最大駐留集大小——它是程序當前在RAM中持有的記憶體量。
但是,與tracemalloc模組不同的是,資源模組不隨時間跟蹤使用情況—它只提供點取樣。因此,我們需要實現一種方法來隨時間對記憶體使用情況進行取樣。

首先,我們定義一個類來執行記憶體監控:
  1. import resource
  2. from time import sleep

  3. class MemoryMonitor:
  4.   def __init__(self):
  5.     self.keep_measuring = True

  6.   def measure_usage(self):
  7.     max_usage = 0
  8.     while self.keep_measuring:
  9.       maxmax_usage = max(
  10.       max_usage,
  11.       resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
  12.       )
  13.     sleep(0.1)

  14.    return max_usage
複製代碼

在這個類的例項上呼叫measure_usage()時,它將進入一個迴圈,每0.1秒測量一次記憶體使用情況。將跟蹤記憶體使用量的任何增加,並在迴圈退出時返回最大記憶體分配。


但是什麼告訴迴圈退出呢?我們在哪裡呼叫被監視的程式碼?我們在單獨的執行緒中完成。

  1. from concurrent.futures import ThreadPoolExecutor


  2. with ThreadPoolExecutor() as executor:
  3.   monitor = MemoryMonitor()
  4.   mem_thread = executor.submit(monitor.measure_usage)
  5.   try:
  6.     fn_thread = executor.submit(my_analysis_function)
  7.     result = fn_thread.result()

  8.   finally:
  9.     monitor.keep_measuring = False
  10.     max_usage = mem_thread.result()

  11.   print(f"Peak memory usage: {max_usage}")
複製代碼

ThreadPoolExecutor為提交要線上程中執行的任務提供了一種方便的方法。我們向執行程式提交兩個任務——監視器和my_analysis_function(如果分析函式需要額外的引數,可以通過提交呼叫傳入它們)。對fn_thread.result()的呼叫將被阻塞,直到分析函式完成並獲得其結果,此時我們可以通知監視器停止並獲得最大記憶體。try/finally模組確保瞭如果分析函式丟擲異常,記憶體執行緒仍然會被終止。
使用這種方法,我們可以有效地隨時間對記憶體使用情況進行抽樣。大部分工作將在主分析執行緒中完成;但是每0.1秒,監視器執行緒就會被喚醒,進行一次記憶體測量,如果記憶體使用量增加就將其儲存,然後返回睡眠狀態。

英文原文:
https://medium.com/survata-engineering-blog/monitoring-memory-usage-of-a-running-python-program-49f027e3d1ba

 

臉書網友討論
*滑块验证:
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-4-26 00:12 , Processed in 0.086494 second(s), 25 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表