Pythonにおけるマルチプロセッシングによる並行実行:詳細解説


Pythonにおけるマルチプロセッシングによる並行実行:詳細解説

今回取り上げる「multiprocessing」は、Pythonでマルチプロセスによる並行処理を簡単に行うためのライブラリです。このライブラリを使用することで、以下の利点が得られます。

  • CPUコアの有効活用: マルチコアCPUの性能を最大限に引き出し、処理速度を向上させることができます。
  • I/O待ち時間の削減: I/O待ちが多いタスクを別プロセスで実行することで、全体の処理時間を短縮することができます。
  • 複雑なタスクの分割: 複雑なタスクを複数の小さなタスクに分割し、それぞれを別プロセスで実行することで、処理を効率化することができます。

multiprocessing の基本的な使い方

multiprocessingを使用するには、以下の手順が必要です。

  1. import: 最初に、multiprocessingライブラリをインポートする必要があります。
import multiprocessing
  1. Poolの作成: 処理を実行するワーカープロセスのプールを作成します。プールサイズは、CPUコア数と同じに設定するのが一般的です。
pool = multiprocessing.Pool(processes=4)
  1. 関数の定義: 並行処理したい関数を定義します。この関数は、各ワーカープロセスで実行されます。
def func(x):
    # 処理内容
    return result
  1. 処理の実行: Poolオブジェクトの mapメソッドを使用して、関数を並行実行します。引数として、関数と処理したいデータのイテレータを渡します。
results = pool.map(func, data_iterator)
  1. Poolの終了: すべての処理が完了したら、Poolオブジェクトを閉じる必要があります。
pool.close()
pool.join()

multiprocessingライブラリは、基本的な並行処理以外にも、様々な機能を提供しています。

  • 非同期実行: apply_asyncメソッドを使用して、関数を非同期に実行することができます。結果を後で取得することもできます。
  • キュー: 異なるプロセス間でデータを共有するために、キューを使用することができます。
  • 共有メモリ: 複数のプロセスが同じメモリ領域にアクセスできるように、共有メモリを使用することができます。
  • エラー処理: エラーが発生した場合に、適切な処理を行うための機能が提供されています。

マルチプロセッシングの注意点

マルチプロセッシングを使用する際には、以下の点に注意する必要があります。

  • 処理対象: マルチプロセッシングは、CPUバウンドタスクに対して効果を発揮します。I/O待ちが多いタスクには、あまり効果がない場合があります。
  • オーバーヘッド: マルチプロセスを作成・終了するには、ある程度のオーバーヘッドがかかります。短時間実行するようなタスクには向いていません。
  • メモリ: マルチプロセスは、それぞれ独立したメモリ空間を持っています。そのため、メモリ使用量が多くなる可能性があります。
  • デバッグ: マルチプロセスプログラムは、デバッグが難しい場合があります。

multiprocessingは、Pythonで並行処理を簡単かつ効率的に実現するための強力なライブラリです。CPUコアの性能を最大限に引き出し、処理速度を向上させたい場合に最適です。



この例では、2つのプロセスを使用して、1から100までの数の平方数と立方数を計算します。

import multiprocessing

def square(x):
    return x * x

def cube(x):
    return x * x * x

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=2)

    squares = pool.map(square, range(101))
    cubes = pool.map(cube, range(101))

    print("Squares:", squares)
    print("Cubes:", cubes)

例2:ファイルの読み込み

この例では、3つのプロセスを使用して、3つのファイルを同時に読み込みます。

import multiprocessing

def read_file(filename):
    with open(filename, 'r') as f:
        content = f.read()
    return content

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)

    results = pool.map(read_file, ['file1.txt', 'file2.txt', 'file3.txt'])

    for result in results:
        print(result)

例3:非同期実行

この例では、非同期にsquare関数を3回実行し、結果を後で取得します。

import multiprocessing

def square(x):
    return x * x

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)

    async_results = [pool.apply_async(square, args=(x,)) for x in range(3)]

    for async_result in async_results:
        result = async_result.get()
        print(result)


  • 利点:
    • 軽量で、コンテキストの切り替えが速いため、CPU使用率が低いタスクやI/O待ちが多いタスクに適しています。
    • GILの影響を受けないため、CPython環境でもマルチコアCPUの性能をある程度活かせます。
    • multiprocessingよりもシンプルで軽量です。
  • 欠点:
    • GILの影響により、CPUバウンドタスクの実行速度は、マルチコアCPUの性能を十分に活かせません。
    • 複数のスレッドが同時に同じオブジェクトにアクセスしようとすると、競合状態が発生する可能性があります。
  • multiprocessingとの比較:
    • 処理速度:CPUバウンドタスクの場合はmultiprocessingの方が高速、I/O待ちが多いタスクの場合は同程度
    • 複雑性:multiprocessingよりもシンプル
    • 共有メモリ:スレッド間でデータを共有する場合は、同期処理が必要
  • 適したユースケース:
    • I/O待ちが多いタスク
    • シンプルな並行処理
    • 軽量な処理が必要な場合

concurrent.futuresモジュール

  • 利点:
    • 柔軟性が高く、様々な種類の並行処理に対応できます。
    • コールバック関数やキューを使用して、非同期処理や結果の受け取りを柔軟に行うことができます。
    • multiprocessingと異なり、Windowsでも動作します。
  • 欠点:
    • multiprocessingよりもオーバーヘッドが大きい場合があります。
    • スレッドベースの並行処理であるため、GILの影響を受けます。
  • multiprocessingとの比較:
    • 柔軟性:concurrent.futuresの方が柔軟
    • 共有メモリ:プロセス間でデータを共有する場合は、multiprocessingを使用する必要があります。
  • 適したユースケース:
    • 柔軟な並行処理が必要な場合
    • Windows環境で並行処理を行う場合

asyncioモジュール

  • 利点:
    • ネットワークやファイル入出力などの非同期処理に非常に効率的です。
    • シングルスレッドで処理を実行するため、GILの影響を受けません。
    • 高いスケーラビリティとパフォーマンスを発揮します。
  • 欠点:
    • CPUバウンドタスクには適していません。
  • 適したユースケース:
    • ネットワークやファイル入出力などの非同期処理

上記以外にも、以下のような並行処理ライブラリが存在します。

  • Joblib: 科学計算に特化したライブラリ
  • Dask: 分散処理に特化したライブラリ
  • Ray: 分散型 AI フレームワーク

それぞれのライブラリには、得意分野や特徴がありますので、具体的なユースケースに合わせて最適なライブラリを選択することが重要です。

multiprocessingは、Pythonで並行処理を簡単かつ効率的に実現するための強力なライブラリですが、万能ではありません。