Pythonの multiprocessing を使って、どれだけ時間短縮になるか計測してみた
Pythonの並列処理ってどうなの?
Pythonで並列処理といえば、よくこんな話を聞きます。
「GILがあるから、マルチスレッドは意味ないよ」
これは一理あります。
というわけで今回は、実際にマルチプロセスを使って処理時間がどれだけ短縮されるかを測ってみました!
🔒 GILってなに?
GIL(Global Interpreter Lock) はPython(特にCPython)にある仕組みで、
「1つのインタプリタで、同時に1つのスレッドしか動かさない」という制限です。
たとえばこんな感じ👇
1. スレッドAが動いてる
2. スレッドBも動かしたい
3. ちょっと待って、Aが終わるまでストップ!
4. Aが完了した
5. 次はB
つまり、スレッドをいくら立てても、実際には1個ずつしか動けないということです。
🤔 それってマルチスレッドの意味なくない?
→ そうなんです。
特にCPUをフルで使うような処理(CPUバウンドな処理)では、スレッドを使っても速くならないんです。
ただし例外もあります。以下のような「I/Oバウンドな処理」では、GILが一時的に解放されるため、スレッドでも並列化の効果があります。
1. ファイルの読み書き
2. ネットワーク通信(APIのレスポンス待ちなど)
GILを回避して並列処理したいなら?
→ 答えは multiprocessing を使うこと。
Pythonには multiprocessing という標準ライブラリがあり、別のPythonプロセスを複数立ち上げて処理を分散することができます。
これならGILの制約を受けずに、複数コアを同時に使って処理を並列化できます!
🧪 実際に計測してみた
マルチプロセスにすれば本当に速くなるのか実測してみます!
処理内容:素数カウント
import time
import multiprocessing
def count_primes(n):
count = 0
for i in range(2, n):
for j in range(2, int(i**0.5) + 1):
if i % j == 0:
break
else:
count += 1
return count
def run_single():
start = time.perf_counter()
inputs = [200_000 + i * 1000 for i in range(16)]
[count_primes(n) for n in inputs]
end = time.perf_counter()
return f'{end - start:.2f} sec'
def run_multi():
start = time.perf_counter()
inputs = [200_000 + i * 1000 for i in range(16)]
with multiprocessing.Pool(processes=8) as pool:
pool.map(count_primes, inputs)
end = time.perf_counter()
return f'{end - start:.2f} sec'
if __name__ == '__main__':
print(f'シングルプロセス: {run_single()}')
print(f'マルチプロセス: {run_multi()}')
シングルプロセス: 2.70 sec
マルチプロセス: 0.49 sec
マルチプロセスのほうが約5.5倍速く処理できたことがわかりました!
今回の処理はそこまで重くないですが、それでもこの差!
処理がもっと重くなれば、マルチプロセスの効果はさらに顕著になります。
シングルで30秒かかる処理 → マルチなら5〜6秒で終わることも
実際の業務(データ集計・画像処理・大規模計算)では体感できるレベルで差が出ます!
✅ まとめ
- PythonにはGILの制約があるけど、マルチプロセスで回避できる
- multiprocessing を使えば、複数コアを活かして処理を高速化できる
- 数行のコードで劇的にパフォーマンス改善できるケースもある
最後に
「Pythonは遅い」「並列処理できない」と思われがちですが、正しく使えば十分高速化できます。
まずは手元で試してみて、「本当に速くなるのか?」を自分の目で確かめてみましょう!