1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonにおける並列処理と並行処理

Posted at

並列処理についての個人的なまとめ。

CPUバウンドとI/Oバウンド

  • 科学計算を行う様なプログラムでディスクとの入出力は行わない、計算に時間がかなりかかり、cpuに負担をかけるようなプログラム→CPUバウンド
    例:数学演算、画像処理、ビデオ処理
  • データが大量に保存されたディスクから任意のドキュメントを返すようなプログラム、CPUでの処理は少ない→IOバウンド
    APサーバーは一般的にCPUバウンドになりがち、反対にDBサーバーはIOバウンドになりがち
    例:ファイルの読み書きやネットワークからの応答待ち

識別方法

  • CPUバウンド
    • cpu使用率が常に高いのに、アプリケーションの処理速度が低下している場合
    • マルチスレッドの処理の効果が低い場合
  • IOバウンド
    • cpuの使用率が低い場合
    • ディスクの使用率やネットワークのトラフィックが高い場合
    • I/O待機時間が長い、システムがデータの読み書きを待っている時間が長い場合

それぞれの対処法

  • CPUバウンド
     プロセスの進行速度がcpuの速度によって制限されているので、コアの周波数をより高くするか、並列化して1コアでなく複数コアで処理する
    コア数以上の並列化を行ってしまうとコンテキストスイッチの時間がかかってしまうので、基本的に並列化はコア数程度にしとくといいらしい

  • IOバウンド

コンテキストスイッチとは

Linuxなどでは多数のプロセスを同時に動作させることができる(マルチタスク)。一般的に一つのプロセッサは同時に複数のプログラムを実行することができないが、複数のプロセスを短い時間で切り替えながら実行することで、体感上は複数のプロセスが同時に実行されているように見えている。
プロセスを切り替える際に処理中のデータが失われないように現在のプロセスが使用しているレジスタやフラグなどの状態を保存し、復帰できる様にする必要がある。
この状態のcpuのことをコンテキスト、保存と復帰をおこない、コンテキストを切り替えることをコンテキストスイッチと呼ぶ。

並列処理と並行処理の違い

並列処理

複数の処理を複数CPUで行う

並行処理

複数の処理を1CPUで行う

CPUバウンドでは並行処理は効果なし

多くの処理はデータを読み込んでcpuで処理をすることが多いのでIO→cpuの流れにある。
この処理がIOで重たいのか、cpuで重たいのかによって並列化するのか、並行化するのかが分かれるらしい。
IOバウンドならIO待ちの間にcpuバウンドな処理を実行しておくことで処理が早くなるので、並行化で問題ないが、cpuバウンドな状態だと、cpuは同時に一つしか使えないため詰まってしまう。
並行処理が有効な例:複数のs3ファイルをローカルに落とす
並列処理が有効な例:数値計算など

Pythonは並列処理が苦手

Global Interpreter Lock(GIL)

goなどの言語と違い、pythonは一つのプロセス内で1つのスレッドでしかバイトコードを実行できないらしい
並列処理における難しさを取り除いているとも言える
並列処理の難しさの例:

  • デッドロック(プロセスやスレッドがお互いのリソースの解放を待ち続けて、それぞれの処理が進行しなくなる状態)
  • cpuが変数を書いている時にもう一つの変数がその変数を呼び出してしまい、データが壊れる
    スクリーンショット 2024-12-01 12.24.45.png
    ※これを外そうという動きもあるらしい

どう並列化するの?

  • プロセスを分ける
    普通の言語では一つのプロセスでいくつかのスレッドを建てて並列化
    pythonはそれができないので、そもそもプロセスを分けてしまう
    pythonにおける並列処理の書き方の例
from concurrent.futures import ProcessPoolExecutor
from random import random
from time import sleep
def func(key):
 sleep(random())
 print(key)
def do_parallel():
 with ProcessPoolExecutor() as executor:
 for key in [1, 2, 3]:
 executor.submit(func, key)

if __name__ == '__main__':
 do_parallel()

かなり簡単にかける

裏の処理はちょっと複雑...

  • unix系
    forkシステムコールで単純にプロセスをコピーする
  • mac,windows
    新規にプロセス作り、そこでsubmitを読んだオブジェクトからpickle/unpikleで転送
    →pickle化できないオブジェクトが欠落してしまう...
    • ファイルオブジェクト
    • DBコネクション
    • s3クライアント
    • lambda関数

まとめ

Pythonでの並列化の問題点について改めて学ぶことができた。
参考にした動画ではRustやほかの言語についての並列処理についても説明してあったので、また次の機会にまとめたい。

参考:

  • GO TechTalk #21 並列処理をGo/Rust/Kotlin/Python/JSで解説!思想の違いを体感しよう

  • Linux のしくみを学ぶ - プロセス管理とスケジューリング

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?