目的
Pythonで処理の効率化を図ろうとすると、「逐次処理」「並列処理」「並行処理」「非同期処理」などの言葉を見かけると思います。
ここでは改めてそれぞれがどのように処理を行うのか、イメージや実装例をもとに学習することを目的としています。
前提知識
- コア
- CPU内にある実際にプログラムの処理を行う部分
- プロセス
- 実行中のプログラム
- プロセス間はそれぞれ独立したメモリ領域をもつため、データの共有には向かない
- スレッド
- プロセス内の処理で、CPUコアが処理する単位
- CPUコア数 = 同時実行できるスレッド数
- 1プロセス内に最低1スレッドがあります。
以下は「マルチコア」「マルチプロセス」「マルチスレッド」のイメージ図になります。
複数タスクを処理するときの各処理の概要
逐次処理
1人が複数のタスクを1つずつ処理していくイメージです。
「シングルコア」「シングルスレッド」で処理を行う場合はこれに該当します。
並行処理
1人が複数のタスクを同時に処理していくイメージです。
「マルチスレッド」で処理を行う場合にはこれに該当します。
また、同時に処理といっても、1人しかいないため瞬間的にみると処理しているタスクは1つとるため、「シングルコア」でも利用可能です。
並列処理
風数人が複数のタスクをそれぞれ同時に処理していくイメージです。
並行処理と違い、どの瞬間を切り取っても複数タスクを同時に処理できています。
「マルチコア」「マルチプロセス」で処理を行う場合はこれに該当します。
逆に「シングルコア」で並列処理を行ったとしても、1つのコアではタスクが交互に実行されるだけで実質並列処理と同等になります。
並行処理と並列処理の使い分け
並行処理
Pythonの並行処理は複数のタスクを高速に切り替えて処理を実行しています。
そのため、I/O操作が多い処理に向いています。並行処理を実装することで、I/O待機などの時間を他タスクに切り替えるなど、効率化を図れます。
I/Oバウンドな処理に最適
データベース操作、ファイルシステム操作、WebAPIの利用などネットワーク通信を伴う操作などなど待ち時間が発生する処理に有効
並列処理
Pythonの並列処理は各CPUコアが異なるタスクを同時に実行します。
そのため、CPUの計算能力に依存する処理に向いています。
ただし、プロセスごとに独立したメモリ空間を持つため、プロセス間でのデータの共有には注意が必要です。
CPUバウンドな処理に最適
科学計算や画像・動画処理、暗号解読やデータ分析などなどCPU依存の処理に有効
並行処理でCPUバウンドなタスクを処理した場合のGILの問題
PythonにはGIL(Global Interpreter Lock)と呼ばれるメモリ管理がマルチスレッドによる同時アクセスから保護するシステムがある。
これにより、CPUコアは一度に一つのスレッドだけが実行可能という制限がかかる。
I/Oバウンドなタスクであれば、並行処理でもI/O待機中はGILが解放され、他スレッドに処理が切り替わるためGILは悪影響を及ぼさないが、
CPUバウンドなタスクの場合、GILがスレッド間で頻繁に切り替わるため、並行処理のマルチスレッドの効果が低下してしまう。
GILのロック機構はプロセス間で独立しているため、CPUバウンドのタスクの場合はマルチプロセスを扱える並列処理を使うようにする。
実装例
coming soon...
非同期処理(asyncio)
coming soon...