Pythonで驚きの効率化!5ステップで並列処理をマスター

Pythonで驚きの効率化!5ステップで並列処理をマスター

Pythonで並列処理を学ぶためのステップバイステップガイドPython
この記事は約9分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスは複数のSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Pythonはその汎用性と使いやすさから、科学技術計算からWeb開発まで、様々な場面で利用されています。

その中で、特に処理速度の向上が求められる場合に有効なのが「並列処理」です。

しかし、Pythonでの並列処理は難しそう、と感じている方も多いのではないでしょうか。

今回は、Pythonでの並列処理の基本から応用まで、5つのステップで習得できるよう、具体的な使い方や注意点、カスタマイズ方法まで詳しく解説します。

●Pythonと並列処理の基本

まず始めに、並列処理の基本的な概念と、Pythonでの並列処理の特性について解説します。

○並列処理とは?

並列処理とは、コンピュータが複数の処理を同時に行うことです。

例えば、大量のデータを処理する際や、高負荷な計算を行う際などに利用します。

一つの処理が終わるのを待つことなく、同時に多くの処理を進めることで全体の処理時間を短縮することができます。

○Pythonにおける並列処理の特性

Pythonでは、マルチプロセッシングとマルチスレッディングの2つの方法で並列処理が可能です。

それぞれ特性が異なり、用途に応じて使い分けることが求められます。

○マルチプロセッシングとマルチスレッディング

マルチプロセッシングは、複数のプロセッサ(CPU)を利用して、複数の処理を同時に行うことができます。

一方、マルチスレッディングは、1つのプロセス内で複数のスレッドを作成し、複数の処理を同時に行うことができます。

スレッドはプロセスよりも軽量で、メモリの共有も容易ですが、PythonのGILという制約により、真の同時実行が難しいという特性があります。

これらの特性を理解した上で、Pythonでの並列処理を効率的に行うための具体的な使い方を見ていきましょう。

●Pythonでの並列処理の具体的な使い方

Pythonでの並列処理の具体的な使い方を、マルチプロセッシングとマルチスレッディングの観点から解説します。

○Pythonにおけるマルチプロセッシングの使い方

Pythonでマルチプロセッシングを行うには、「multiprocessing」モジュールを使用します。

それでは、基本的なマルチプロセッシングのサンプルコードを紹介します。

□サンプルコード1:基本的なマルチプロセッシング

import multiprocessing
import time

def worker(n):
    print(f'Worker {n} has started.')
    time.sleep(2)
    print(f'Worker {n} has finished.')

if __name__ == '__main__':
    processes = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

このコードでは、multiprocessingモジュールを使って5つのプロセスを生成しています。

それぞれのプロセスで関数workerが実行され、2秒間待機した後に終了します。

この例では、5つのプロセスが同時に開始され、ほぼ同時に終了するため、全体の実行時間は2秒程度になります。

同じ処理をシングルプロセスで行うと10秒かかる処理が、並列化により2秒で完了することを確認できます。

○Pythonにおけるマルチスレッディングの使い方

Pythonでマルチスレッディングを行うには、「threading」モジュールを使用します。

基本的なマルチスレッディングのサンプルコードを紹介します。

□サンプルコード2:基本的なマルチスレッディング

import threading
import time

def worker(n):
    print(f'Thread {n} has started.')
    time.sleep(2)
    print(f'Thread {n} has finished.')

if __name__ == '__main__':
    threads = []
    for i in range(5):
        t = threading.Thread(target=worker, args=(i,))
        t.start()
        threads.append(t)

    for t in threads:
        t.join()

このコードでは、threadingモジュールを使って5つのスレッドを生成しています。

各スレッドで関数workerが実行され、2秒間待機した後に終了します。

この例では、5つのスレッドが同時に開始され、ほぼ同時に終了するため、全体の実行時間は2秒程度になります。

ただし、これはI/O待ちなどのブロッキング操作が主体の処理において有効な方法であり、CPU密集型の処理にはマルチプロセッシングの方が適しています。

●Pythonでの並列処理の応用例

Pythonでの並列処理は、大量のデータ処理や非同期処理など、様々な場面で活用することができます。

ここではそれぞれの応用例を解説します。

○大量のデータを扱う場合の並列処理の利用

大量のデータを扱う場合、一つずつ順番に処理していくと非常に時間がかかります。

そんな時、並列処理を利用することで、処理時間を大幅に短縮することができます。

それでは、大量のデータを並列に処理するサンプルコードを紹介します。

□サンプルコード3:大量のデータを並列に処理する

from multiprocessing import Pool

def square(n):
    return n ** 2

if __name__ == '__main__':
    with Pool(processes=4) as pool:
        results = pool.map(square, range(10000))
    print(results)

このコードでは、「multiprocessing」モジュールの「Pool」クラスを使って、4つのプロセスを生成しています。

そして、pool.map関数を使って、関数squareを大量のデータ(ここでは0から9999までの整数)に対して並列に適用しています。

この結果、1つずつ順番に処理するよりも大幅に処理時間を短縮することができます。

○非同期処理による効率化

Pythonでは、並列処理だけでなく非同期処理もサポートしています。

非同期処理は、あるタスクが完了するのを待つことなく、次のタスクに進むことができます。

これにより、I/O待ちなどのブロッキング操作を効率的に処理することができます。

□サンプルコード4:非同期処理を行うコード

import asyncio

async def hello(name, delay):
    print(f'Hello, {name}!')
    await asyncio.sleep(delay)
    print(f'Goodbye, {name}!')

if __name__ == '__main__':
    asyncio.run(hello('World', 2))

このコードでは、非同期処理を行うための「asyncio」モジュールを使用しています。

async defで定義した非同期関数hello内で、awaitを使って非同期に待機操作を行っています。

この結果、待機中に他の処理を行うことができ、全体の処理時間を短縮することができます。

●Pythonでの並列処理の注意点と対策

Pythonで並列処理を行う際には、GIL(Global Interpreter Lock)という制約や、並列処理のデメリットについて理解しておく必要があります。

○GIL(Global Interpreter Lock)とその対策

PythonにはGILという制約があり、一度に一つのスレッドしか実行できないという制約があります。

これは、Pythonが内部で状態を保つために導入されたものですが、マルチスレッディングを行う際にはパフォーマンスのボトルネックとなります。

GILの影響を避けるためには、CPU密集型の処理にはマルチプロセッシングを利用する、I/O密集型の処理にはマルチスレッディングや非同期処理を利用する、といった使い分けをすると良いでしょう。

○並列処理のデメリットとその対策

一方、並列処理は全ての処理で利点があるわけではありません。

例えば、並列化にはオーバーヘッドが発生し、プログラムの複雑性が増すというデメリットがあります。

また、データの競合や状態の不整合など、並列処理特有の問題も発生します。

これらのデメリットを避けるためには、必要な場合のみ並列処理を利用し、それ以外の場合は単一スレッドの処理を行うことが望ましいです。

また、並列処理を行う場合には、データのアクセス制御や同期処理などを適切に行うことが重要です。

●Pythonでの並列処理のカスタマイズ方法

Pythonでの並列処理は、マルチプロセッシングとマルチスレッディングの組み合わせによって、様々なパターンでカスタマイズすることができます。

それでは、プロセスとスレッドを組み合わせた並列処理のサンプルコードを紹介します。

○プロセスとスレッドの最適な組み合わせ

下記のコードは、マルチプロセッシングとマルチスレッディングを組み合わせた例です。

これにより、CPUの複数のコアと同時に、各コア内の複数のスレッドを効率的に活用することができます。

□サンプルコード5:プロセスとスレッドを組み合わせた並列処理

from multiprocessing import Process
import threading

def print_num(thread_num, process_num):
    print(f'Thread {thread_num} in process {process_num}')

def create_threads(process_num):
    for i in range(5):
        thread = threading.Thread(target=print_num, args=(i, process_num))
        thread.start()

if __name__ == '__main__':
    for i in range(5):
        process = Process(target=create_threads, args=(i,))
        process.start()

このコードでは、multiprocessing.Processで5つのプロセスを生成し、各プロセス内でthreading.Threadで5つのスレッドを生成しています。

これにより、合計25(5プロセス × 5スレッド)のタスクを同時に実行することができます。

これらの並列処理のテクニックを活用することで、Pythonプログラムのパフォーマンスを大幅に向上させることが可能になります。

まとめ

Pythonでの並列処理について、その基本から応用、注意点、カスタマイズ方法までを学びました。

並列処理は、大量のデータを扱う場合や時間のかかる処理を効率化するための強力な手段です。

しかし、それには注意点もあり、適切な使い方をすることが重要です。

本記事が、Pythonでの並列処理を始める一助となれば幸いです。