PythonのpsutilでCPU使用率を並列的に監視する
Pythonで何か処理を走らせている時に, CPUの使用率を定期的に監視するスクリプトを書いた.
マルチスレッドにすることで, メインの処理を止めずに定期実行できる. 重い処理の計算機負荷の時系列データをレポートしたい時とかに使えるかもしれない.
コード全文は最下に.
ライブラリのインストール
psutilというライブラリを使うことでCPUの使用率をPythonで取得することができる. pip経由でインストールできる.
pip install psutil
基本的な使い方は以下の通り.
# percpu=Trueでコアごとの使用率(%)をリストで返す >>> psutil.cpu_percent(interval=1, percpu=True) [7.0, 1.0, 8.9, 0.0, 5.0, 1.0, 4.0, 0.0] # percpu=Falseで平均の使用率となる >>> psutil.cpu_percent(interval=1, percpu=False) 8.9 # interval(秒)を小さくすると計測時間が短くなるが, 誤差も大きくなる. >>> psutil.cpu_percent(interval=0.5, percpu=True) [6.0, 0.0, 7.8, 1.9, 4.0, 0.0, 5.8, 0.0]
並列処理
今回はネイティブのライブラリであるthreadingを使って並列処理を実装する.
以下のようにスレッドインスタンスの作成と実行を行うことができる.
m = threading.Thread(target=monitor_cpu,args=((initial_time,))) m.start()
threading.Thread()
に渡す引数として, target
には実行するメソッドを渡し, args
には実行メソッドへの引数を渡す.
monitor_cpu
が監視用メソッドである.
監視用メソッド
threading.Event
を使って監視用スレッドの管理をする.
threading.Event
の使い方については以下のページが詳しい.
おまいらのthreading.Eventの使い方は間違っている
threading.Eventとはイベントが発生するまでスレッドを待機させ、他のスレッドからイベントを発生させると待機スレッドが再開する、という使い方をする為のクラスです。
上のページでも説明があるように
wait(timeout)
: イベントが発生するかtimeout
秒経過するまで現在のスレッドを待機させる.
set()
: イベントを発生させる.
のふたつを使用する.
def monitor_cpu(initial_time): print("START monitor_cpu") while not event.wait(1): # 1秒待機, イベントが発生していなければループ内を実行して再び待機 elapsed_time = time.time() - initial_time cpu_percent = psutil.cpu_percent(percpu=True) cpu_percent = '\t'.join(["{:10.4f}".format(v) for v in cpu_percent]) print("time:", int(elapsed_time), cpu_percent) print("END monitor_cpu") # イベントが発生したらループを抜け実行終了 if __name__=="__main__": event = threading.Event() initial_time = time.time() m = threading.Thread(target=monitor_cpu,args=((initial_time,))) m.start() # メイン処理 event.set() # イベントを発生させる.
monitor_cpu
内部では実行間隔を1秒で無限ループを回す. イベントが発生するとループを抜け実行終了する.
ちなみにこの実装はtime.sleep(1)
と終了条件を司るフラグを使っても再現できる. こちらのほうがシンプルで良いかもしれない.
def monitor_cpu(initial_time): print("START monitor_cpu") while flag: time.sleep(1) elapsed_time = time.time() - initial_time cpu_percent = psutil.cpu_percent(percpu=True) cpu_percent = '\t'.join(["{:10.4f}".format(v) for v in cpu_percent]) print("time:", int(elapsed_time), cpu_percent) print("END monitor_cpu") if __name__=="__main__": event = threading.Event() initial_time = time.time() flag = True m = threading.Thread(target=monitor_cpu,args=((initial_time,))) m.start() tmp = 0 for i in range(100000000): tmp = i+i flag = False
コード全体
import threading import time import psutil def monitor_cpu(initial_time): print("START monitor_cpu") while not event.wait(1): elapsed_time = time.time() - initial_time cpu_percent = psutil.cpu_percent(percpu=True) cpu_percent = '\t'.join(["{:10.4f}".format(v) for v in cpu_percent]) print("time:", int(elapsed_time), cpu_percent) print("END monitor_cpu") if __name__=="__main__": event = threading.Event() initial_time = time.time() m = threading.Thread(target=monitor_cpu,args=((initial_time,))) m.start() tmp = 0 for i in range(100000000): tmp = i+i event.set()
実行結果
左側から時刻(秒), CPU1の使用率(%), CPU2の使用率 ... CPU8の使用率 と並んでいる.
START monitor_cpu time: 1 52.0 1.0 29.7 1.0 28.7 2.0 30.4 0.0 time: 2 45.0 3.0 36.3 3.0 32.4 1.0 29.4 1.0 time: 3 43.6 2.0 31.0 1.0 33.7 0.0 27.7 2.0 time: 4 45.5 2.0 25.2 1.0 22.8 0.0 19.8 0.0 time: 5 41.6 1.0 26.0 1.0 26.7 1.0 20.8 1.0 time: 6 46.1 5.0 34.7 3.0 38.0 3.0 31.7 4.0 time: 7 63.0 10.0 52.5 10.0 55.4 10.9 55.0 10.0 time: 8 51.5 6.9 36.0 4.9 41.2 5.0 39.6 4.9 time: 9 55.0 0.0 20.6 1.0 26.0 0.0 14.9 0.0 time: 10 49.5 2.9 22.8 1.0 25.2 1.0 23.5 1.0 time: 11 43.6 1.0 32.7 0.0 28.3 0.0 23.8 1.0 time: 12 47.5 2.9 22.8 1.0 20.6 2.0 25.0 2.0 END monitor_cpu