ホームページ  >  記事  >  バックエンド開発  >  Pythonマルチプロセスの適用方法

Pythonマルチプロセスの適用方法

PHPz
PHPz転載
2023-05-25 09:28:051493ブラウズ

並列およびシリアル コンピューティング

解決すべき大きな問題があり、あなたが一人であると想像してください。 8 つの異なる数値の平方根を計算する必要があります。職業はなんですか?あまり選択肢はありません。最初の数値から始めて結果を計算します。それから他の人に移ります。

数学が得意で喜んで手伝ってくれる友達が 3 人いる場合はどうしますか?それぞれが 2 つの数値の平方根を計算し、仕事量が友人間で均等に分散されるため、仕事が簡単になります。これは、問題がより早く解決されることを意味します。

わかりました、すべて分かりましたか?これらの例では、各フレンドは CPU のコアを表します。最初の例では、タスク全体が順番に解決されます。これは シリアル計算 と呼ばれます。 2 番目の例では、合計 4 つのコアを使用しているため、Parallel Computing を使用しています。並列コンピューティングには、並列プロセス、またはプロセッサの複数のコアに分割されたプロセスの使用が含まれます。

Pythonマルチプロセスの適用方法

並列プログラミング モデル

並列プログラミングとは何かを確立しましたが、それをどのように使用するのでしょうか?並列コンピューティングには、プロセッサの複数のコアにわたって複数のタスクを実行することが含まれると前に説明しました。これは、これらのタスクが同時に実行されることを意味します。並列化を進める前に、いくつかの問題を考慮する必要があります。たとえば、計算を高速化できる他の最適化はありますか?

さて、並列化が最適なソリューションであることは当然のことだと考えましょう。並列コンピューティングには 3 つの主なモードがあります:

  • 完全並列 。タスクは独立して実行でき、相互に通信する必要はありません。

  • 共有メモリ並列。プロセス (またはスレッド) は通信する必要があるため、グローバル アドレス空間を共有します。

  • #メッセージの受け渡し。プロセスは必要に応じてメッセージを共有する必要があります。

この記事では、最も単純でもある最初のモデルについて説明します。

Python マルチプロセッシング: Python のプロセスベースの並列処理

Python で並列処理を実現する 1 つの方法は、

multiprocessing モジュールを使用することです。 multiprocessing このモジュールを使用すると、それぞれに独自の Python インタープリターを備えた複数のプロセスを作成できます。したがって、Python マルチプロセッシングはプロセスベースの並列処理を実装します。

同じく Python に組み込まれている

threading などの他のライブラリについて聞いたことがあるかもしれませんが、それらの間には重要な違いがあります。 multiprocessing モジュールは新しいプロセスを作成し、threading は新しいスレッドを作成します。

マルチプロセスを使用する利点

「なぜマルチプロセスを選択するのですか?」と疑問に思われるかもしれません。マルチプロセスは、複数のタスクを順次ではなく並行して実行することにより、プログラムの効率を大幅に向上させることができます。 。似た用語にマルチスレッドがありますが、それらは異なります。

プロセスはメモリにロードされるプログラムであり、他のプロセスとメモリを共有しません。スレッドはプロセス内の実行単位です。複数のスレッドがプロセス内で実行され、プロセスのメモリ空間を相互に共有します。

Python のグローバル インタープリター ロック (GIL) では、インタープリターの下で一度に 1 つのスレッドしか実行できません。つまり、Python インタープリターが必要な場合は、マルチスレッドのパフォーマンス上の利点を享受できません。これが、Python でスレッド処理よりもマルチ処理の方が有利である理由です。各プロセスには、割り当てられた命令を実行する独自のインタプリタがあるため、複数のプロセスを並行して実行できます。さらに、オペレーティング システムはプログラムを複数のプロセスで調べて個別にスケジュールします。つまり、プログラムがコンピュータ リソース全体のより大きなシェアを占めることになります。したがって、プログラムが CPU バウンドである場合、マルチ処理は高速になります。プログラム内に大量の I/O がある状況では、ほとんどの時間、プログラムは I/O が完了するのを待機しているため、スレッドの方が効率的である可能性があります。ただし、複数のプロセスは同時に実行されるため、通常はより効率的です。

マルチプロセッシングの利点は次のとおりです:

  • CPU を大量に使用するタスクを処理する際の CPU の使用効率の向上

  • スレッドと比較して、子スレッドに対する制御が強化されています

  • コーディングが簡単

最初の利点はパフォーマンスに関連しています。マルチプロセッシングでは新しいプロセスが作成されるため、タスクを他のコア間で分割することで CPU の計算能力をより有効に活用できます。最近のほとんどのプロセッサはマルチコアであり、コードを最適化すれば、並列コンピューティングによって時間を節約できます。

2 番目の利点は、マルチスレッドの代替手段です。スレッドはプロセスではないため、これには影響があります。スレッドを作成した場合、通常のプロセスと同様にスレッドを終了したり、中断したりすることは危険です。マルチプロセッシングとマルチスレッドの比較はこの記事の範囲を超えているため、マルチプロセッシングとマルチスレッドの違いについては、後で別の記事を書きます。

マルチプロセッシングの 3 番目の利点は、処理しようとしているタスクが並列プログラミングに適しているため、実装が簡単であることです。

Python マルチプロセスの開始

いよいよ Python コードを作成する準備が整いました。

Python マルチプロセッシングの中核的な側面を説明するために使用する非常に基本的な例から始めます。この例では、

  • parentoften という 2 つのプロセスがあります。親プロセスは 1 つだけあり、複数の子プロセスを持つことができます。

  • #子プロセス。これは親プロセスによって生成されます。各子プロセスは新しい子プロセスを持つこともできます。

child プロシージャを使用して特定の関数を実行します。このようにして、parent は実行を継続できます。

簡単な Python マルチプロセスの例

この例で使用するコードは次のとおりです:

from multiprocessing import Process

def bubble_sort(array):
    check = True
    while check == True:
      check = False
      for i in range(0, len(array)-1):
        if array[i] > array[i+1]:
          check = True
          temp = array[i]
          array[i] = array[i+1]
          array[i+1] = temp
    print("Array sorted: ", array)

if __name__ == '__main__':
    p = Process(target=bubble_sort, args=([1,9,4,5,2,6,8,4],))
    p.start()
    p.join()
このスニペットでは、

bubble_sort という名前のプロセスを定義します。 (配列)###。この関数は、バブル ソート アルゴリズムの非常に単純な実装です。それが何であるか分からなくても、重要ではないので心配しないでください。知っておくべき重要なことは、これは何かを行う関数であるということです。 プロセス クラス

multiprocessing

から、クラス Process をインポートします。このクラスは、別のプロセスで実行されるアクティビティを表します。実際、いくつかのパラメータを渡していることがわかります:

  • target=bubble_sort

    これは、新しいプロセスがその bubble_sort を実行することを意味します。 Function

  • args=([1,9,4,52,6,8,4],)

    、これはターゲット関数にパラメータとして渡されます

  • Process クラスのインスタンスを作成したら、プロセスを開始するだけです。これは、
p.start()

を記述することで実行されます。この時点でプロセスが始まります。 終了する前に、子プロセスが計算を完了するまで待つ必要があります。

join()

メソッドはプロセスが終了するまで待機します。 この例では、子プロセスを 1 つだけ作成します。ご想像のとおり、

Process

クラスでインスタンスをさらに作成することで、さらに多くの子プロセスを作成できます。 プロセス プール クラス

CPU 集中型のタスクを処理するために複数のプロセスを作成する必要がある場合はどうすればよいでしょうか?常に明示的に開始して終了を待つ必要があるのでしょうか?ここでの解決策は、

Pool

クラスを使用することです。

Pool

クラスを使用すると、ワーカー プロセスのプールを作成できます。次の例では、その使用方法を見ていきます。新しい例は次のとおりです。 <pre class="brush:php;toolbar:false">from multiprocessing import Pool import time import math N = 5000000 def cube(x):     return math.sqrt(x) if __name__ == &quot;__main__&quot;:     with Pool() as pool:       result = pool.map(cube, range(10,N))     print(&quot;Program finished!&quot;)</pre> このコード スニペットには、整数だけを受け取り、その平方根を返す

cube(x)

関数があります。とてもシンプルですよね? 次に、プロパティを指定せずに

Pool

クラスのインスタンスを作成します。デフォルトでは、Pool クラスは CPU コアごとに 1 つのプロセスを作成します。次に、いくつかのパラメーターを指定して map メソッドを実行します。

map

メソッドは、提供する反復可能オブジェクトの各要素に cube 関数を適用します。この場合、10# からの各数値のリストです。 ## から N まで。 これを行う最大の利点は、リストの計算が並行して実行されることです。

joblib

Package

joblib

は、並列コンピューティングを容易にするツールのセットです。マルチプロセス用の汎用サードパーティライブラリです。また、キャッシュ機能とシリアル化機能も提供します。

joblib パッケージをインストールするには、ターミナルで次のコマンドを使用します:

pip install joblib
前の例を、joblib を使用する次の例に変換できます:

from joblib import Parallel, delayed
 
def cube(x):
    return x**3
 
start_time = time.perf_counter()
result = Parallel(n_jobs=3)(delayed(cube)(i) for i in range(1,1000))
finish_time = time.perf_counter()
print(f"Program finished in {finish_time-start_time} seconds")
print(result)
実際、それが何をするのか直感的に見てください。 layed()関数は、関数呼び出しの「遅延」バージョンを生成する別の関数のラッパーです。これは、関数が呼び出されてもすぐには実行されないことを意味します。

然后,我们多次调用delayed函数,并传递不同的参数集。例如,当我们将整数1赋予cube函数的延迟版本时,我们不计算结果,而是分别为函数对象、位置参数和关键字参数生成元组(cube, (1,), {})

我们使用Parallel()创建了引擎实例。当它像一个以元组列表作为参数的函数一样被调用时,它将实际并行执行每个元组指定的作业,并在所有作业完成后收集结果作为列表。在这里,我们创建了n_jobs=3Parallel()实例,因此将有三个进程并行运行。

我们也可以直接编写元组。因此,上面的代码可以重写为:

result = Parallel(n_jobs=3)((cube, (i,), {}) for i in range(1,1000))

使用joblib的好处是,我们可以通过简单地添加一个附加参数在多线程中运行代码:

result = Parallel(n_jobs=3, prefer="threads")(delayed(cube)(i) for i in range(1,1000))

这隐藏了并行运行函数的所有细节。我们只是使用与普通列表理解没有太大区别的语法。

充分利用 Python多进程

创建多个进程并进行并行计算不一定比串行计算更有效。对于 CPU 密集度较低的任务,串行计算比并行计算快。因此,了解何时应该使用多进程非常重要——这取决于你正在执行的任务。

为了让你相信这一点,让我们看一个简单的例子:

from multiprocessing import Pool
import time
import math

N = 5000000

def cube(x):
    return math.sqrt(x)

if __name__ == "__main__":
    # first way, using multiprocessing
    start_time = time.perf_counter()
    with Pool() as pool:
      result = pool.map(cube, range(10,N))
    finish_time = time.perf_counter()
    print("Program finished in {} seconds - using multiprocessing".format(finish_time-start_time))
    print("---")
    # second way, serial computation
    start_time = time.perf_counter()
    result = []
    for x in range(10,N):
      result.append(cube(x))
    finish_time = time.perf_counter()
    print("Program finished in {} seconds".format(finish_time-start_time))

此代码段基于前面的示例。我们正在解决同样的问题,即计算N个数的平方根,但有两种方法。第一个涉及 Python 进程的使用,而第二个不涉及。我们使用time库中的perf_counter()方法来测量时间性能。

在我的电脑上,我得到了这个结果:

> python code.py
Program finished in 1.6385094 seconds - using multiprocessing
---
Program finished in 2.7373942999999996 seconds

如你所见,相差不止一秒。所以在这种情况下,多进程更好。

让我们更改代码中的某些内容,例如N的值。 让我们把它降低到N=10000,看看会发生什么。

这就是我现在得到的:

> python code.py
Program finished in 0.3756742 seconds - using multiprocessing
---
Program finished in 0.005098400000000003 seconds

发生了什么?现在看来,多进程是一个糟糕的选择。为什么?

与解决的任务相比,在进程之间拆分计算所带来的开销太大了。你可以看到在时间性能方面有多大差异。

以上がPythonマルチプロセスの適用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。