multiprocessingとthreadingライブラリのベンチマーク
Pythonにはマルチプロセスを実現するためのmultiprocessingライブラリ,マルチスレッドを実現するためのthreadingライブラリがある.今回はこれらのライブラリを使って,CPU boundな処理とI/O boundな処理を実行した際の処理時間を計測した.
計測プログラム
以下の計測プログラムを書いた.I/O boundな処理として,1秒間sleepする関数を用意し,CPU boundな処理として,1から与えられた数までの和を計算する関数を用意した.
from multiprocessing import Process from threading import Thread from datetime import datetime from random import randint import sys import time import os N = 3628800 def io(d): # ignore d time.sleep(1) def cpu(d): n = 0 while d > 0: n += d d -= 1 def bench(thpr, work, njob): f = cpu if work == "cpu" else io js = [thpr(target=f, args=(N//njob, )) for _ in range(njob)] s = datetime.now() for j in js: j.start() for j in js: j.join() e = datetime.now() d = e - s return d.total_seconds() def main(): if len(sys.argv) != 5: print(f"Usage: pythnon {sys.argv[0]} [thread|process] [cpu|io] NJOB NTRY") return thpr = sys.argv[1] work = sys.argv[2] njob = int(sys.argv[3]) ntry = int(sys.argv[4]) if thpr not in ["thread", "process"]: print("Only thread or process is allowed") return if work not in ["cpu", "io"]: print("Only cpu or io is allowed") return if njob <= 0: print("NJOB should be > 0") return if ntry <= 0: print("NTRY should be > 0") return thpr = Process if thpr == "process" else Thread result = sum([bench(thpr, work, njob) for _ in range(ntry)]) / ntry print(f"{result:.5f}") if __name__ == "__main__": main()
以下のように使います.
$ python bench.py thread cpu 5 2
これは,5スレッドでCPU boundな処理を2回実行した際の処理時間の平均を出力してくれます.
測定結果
前述のプログラムを使って,以下の4つの場合について処理時間を測定した.
- multiprocessing, I/O bound
- multiprocessing, CPU bound
- threading, I/O bound
- threading, CPU bound
各ケースについて,プロセス数/スレッド数を1から10に変動させて,それぞれ20回測定した.測定結果は以下の通り.
multiprocessing, I/O bound
まあ,妥当ではという感想.逐次的に10回実行するなら10秒かかるところ,マルチプロセス(10プロセス)にすることで,約1秒で全タスクが終わる.
multiprocessing, CPU bound
1プロセスのときは0.6秒前後だったのが,2プロセスのときは0.3秒前後と約半分になった.しかし,そこで頭打ちを食らい,以降,プロセス数を増やしても性能向上せず.使っているマシンのCPUが2コアだから,こちらもまあ妥当では.
threading, I/O bound
multiprocessing, I/O boundとだいたい似た傾向.multiprocessing, I/O boundに比べて,threadingの場合はスレッド数を増やしても,処理時間が上がらず,比較的安定している.スレッドの方がコンテキストスィッチが軽量だから?
threading, CPU bound
なぜこうなった.PythonがGILのせいで,CPU boundな処理をマルチスレッドにしても性能向上が見込めないことはどこかで耳にしたことがあるが,性能劣化するとは思わなかった.やはり,GIL絡みなのか?要調査.
GILに関する良さげな文献1を見つけたのであとで読む.