解決すべき大きな問題があり、あなたが一人であると想像してください。 8 つの異なる数値の平方根を計算する必要があります。職業はなんですか?あまり選択肢はありません。最初の数値から始めて結果を計算します。それから他の人に移ります。
数学が得意で喜んで手伝ってくれる友達が 3 人いる場合はどうしますか?それぞれが 2 つの数値の平方根を計算し、仕事量が友人間で均等に分散されるため、仕事が簡単になります。これは、問題がより早く解決されることを意味します。
わかりました、すべて分かりましたか?これらの例では、各フレンドは CPU のコアを表します。最初の例では、タスク全体が順番に解決されます。これは シリアル計算 と呼ばれます。 2 番目の例では、合計 4 つのコアを使用しているため、Parallel Computing を使用しています。並列コンピューティングには、並列プロセス、またはプロセッサの複数のコアに分割されたプロセスの使用が含まれます。
並列プログラミングとは何かを確立しましたが、それをどのように使用するのでしょうか?並列コンピューティングには、プロセッサの複数のコアにわたって複数のタスクを実行することが含まれると前に説明しました。これは、これらのタスクが同時に実行されることを意味します。並列化を進める前に、いくつかの問題を考慮する必要があります。たとえば、計算を高速化できる他の最適化はありますか?
さて、並列化が最適なソリューションであることは当然のことだと考えましょう。並列コンピューティングには 3 つの主なモードがあります:
完全並列 。タスクは独立して実行でき、相互に通信する必要はありません。
共有メモリ並列。プロセス (またはスレッド) は通信する必要があるため、グローバル アドレス空間を共有します。
#メッセージの受け渡し。プロセスは必要に応じてメッセージを共有する必要があります。
multiprocessing モジュールを使用することです。 multiprocessing このモジュールを使用すると、それぞれに独自の Python インタープリターを備えた複数のプロセスを作成できます。したがって、Python マルチプロセッシングはプロセスベースの並列処理を実装します。
threading などの他のライブラリについて聞いたことがあるかもしれませんが、それらの間には重要な違いがあります。
multiprocessing モジュールは新しいプロセスを作成し、
threading は新しいスレッドを作成します。
2 番目の利点は、マルチスレッドの代替手段です。スレッドはプロセスではないため、これには影響があります。スレッドを作成した場合、通常のプロセスと同様にスレッドを終了したり、中断したりすることは危険です。マルチプロセッシングとマルチスレッドの比較はこの記事の範囲を超えているため、マルチプロセッシングとマルチスレッドの違いについては、後で別の記事を書きます。
マルチプロセッシングの 3 番目の利点は、処理しようとしているタスクが並列プログラミングに適しているため、実装が簡単であることです。
いよいよ Python コードを作成する準備が整いました。
Python マルチプロセッシングの中核的な側面を説明するために使用する非常に基本的な例から始めます。この例では、
parent
often という 2 つのプロセスがあります。親プロセスは 1 つだけあり、複数の子プロセスを持つことができます。
#子プロセス。これは親プロセスによって生成されます。各子プロセスは新しい子プロセスを持つこともできます。
child プロシージャを使用して特定の関数を実行します。このようにして、
parent は実行を継続できます。
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 という名前のプロセスを定義します。 (配列)###。この関数は、バブル ソート アルゴリズムの非常に単純な実装です。それが何であるか分からなくても、重要ではないので心配しないでください。知っておくべき重要なことは、これは何かを行う関数であるということです。 プロセス クラス
から、クラス Process
をインポートします。このクラスは、別のプロセスで実行されるアクティビティを表します。実際、いくつかのパラメータを渡していることがわかります:
これは、新しいプロセスがその bubble_sort
を実行することを意味します。 Function
、これはターゲット関数にパラメータとして渡されます
を記述することで実行されます。この時点でプロセスが始まります。 終了する前に、子プロセスが計算を完了するまで待つ必要があります。
メソッドはプロセスが終了するまで待機します。 この例では、子プロセスを 1 つだけ作成します。ご想像のとおり、
クラスでインスタンスをさらに作成することで、さらに多くの子プロセスを作成できます。 プロセス プール クラス
クラスを使用することです。
クラスを使用すると、ワーカー プロセスのプールを作成できます。次の例では、その使用方法を見ていきます。新しい例は次のとおりです。 <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__ == "__main__":
with Pool() as pool:
result = pool.map(cube, range(10,N))
print("Program finished!")</pre>
このコード スニペットには、整数だけを受け取り、その平方根を返す
関数があります。とてもシンプルですよね? 次に、プロパティを指定せずに
クラスのインスタンスを作成します。デフォルトでは、Pool
クラスは CPU コアごとに 1 つのプロセスを作成します。次に、いくつかのパラメーターを指定して map
メソッドを実行します。
メソッドは、提供する反復可能オブジェクトの各要素に cube
関数を適用します。この場合、10# からの各数値のリストです。 ## から
N まで。
これを行う最大の利点は、リストの計算が並行して実行されることです。
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=3
的Parallel()
实例,因此将有三个进程并行运行。
我们也可以直接编写元组。因此,上面的代码可以重写为:
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))
这隐藏了并行运行函数的所有细节。我们只是使用与普通列表理解没有太大区别的语法。
创建多个进程并进行并行计算不一定比串行计算更有效。对于 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 サイトの他の関連記事を参照してください。