マルチプロセッシングを使用してデータオブジェクト内のアイテムに関数を適用することでパフォーマンスを高速化できますか?

BernardL

免責事項:私はmultiprocessingSOとドキュメントに関する多数の回答を確認しましたが、質問が本当に古いか(Python 3.Xはそれ以来多くの改善を行っています)、明確な回答が見つかりませんでした。関連する何かを見逃した可能性がある場合は、正しい方向に向けてください。

Jupyter Notebookを実行しているため、フォルダーモジュールで以下のように定義した単純な関数から始めましたが、競合のためmultiprocessing、インポートされた関数でのみ実行できるようです

def f(a):
    return a * 100

いくつかのテストデータを作成し、いくつかのテストを実行しました。

from itertools import zip_longest
from multiprocessing import Process, Pool, Array, Queue
from time import time

from modules.test import *

li = [i for i in range(1000000)]

リスト内包表記:本当に速い

start = time()
tests = [f(i) for i in li]
print(f'Total time {time() - start} s')
>> Total time 0.154066801071167 s

ここでのSOの例の回答:11秒程度

start = time()

results = []
if __name__ == '__main__':

    jobs = 4
    size = len(li)

    heads = list(range(size//jobs, size, size//jobs)) + [size]
    tails = range(0,size,size//jobs)

    pool = Pool(4)
    for tail,head in zip(tails, heads):
        r = pool.apply_async(f, args=(li[tail:head],))
        results.append(r)

    pool.close()
    pool.join() # wait for the pool to be done

print(f'Total time {time() - start} s')
>>Total time 11.087551593780518 s

そしてProcess、上記の例に当てはまるかどうかわからないことがあります。私はよく知らないがmultiprocessing、新しいインスタンスの作成にはいくらかのオーバーヘッドがあることを理解しているが、データが大きくなるにつれて、オーバーヘッドを正当化するはずである。

私の質問は、Python 3.xの現在のパフォーマンスでmultiprocessing、上記と同様の操作を実行する際に使用していることです。もしそうなら、ワークロードの並列化にどのように適用できますか。

私が読んで理解した例のほとんどは、情報を受信する1つのプロセスに実際のアイドル時間があり、並列化するのが理にかなっている場合のWebスクレイピングに使用されますが、リストや辞書などの計算を実行している場合は、どのようにアプローチする必要がありますか。

juanpa.arrivillaga

あなたの例がうまく機能していない理由は、2つのまったく異なることをしているからです。

リスト内包表記では、の各要素にマッピングfしていますli

2番目のケースでは、liリストをjobsチャンクに分割jobしてから、それらのチャンクのそれぞれに関数の時間を適用しますそして今、ではfn * 100元のリストの約1/4のサイズのチャンクを取り、それを100倍します。つまり、シーケンス反復演算子を使用するため、チャンクの100倍のサイズの新しいリストを作成します

>>> chunk = [1,2,3]
>>> chunk * 10
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
>>>

つまり、基本的には、リンゴとオレンジを比較しています。

ただし、マルチプロセッシングには、すぐに使用できるマッピングユーティリティがすでに付属しています。これがより良い比較で、foo.pyと呼ばれるスクリプトです。

import time
import multiprocessing as mp

def f(x):
    return x * 100

if __name__ == '__main__':
    data = list(range(1000000))

    start = time.time()
    [f(i) for i in data]
    stop = time.time()
    print(f"List comprehension took {stop - start} seconds")

    start = time.time()
    with mp.Pool(4) as pool:
        result = pool.map(f, data)
    stop = time.time()
    print(f"Pool.map took {stop - start} seconds")

ここで、実際のパフォーマンス結果をいくつか示します。

(py37) Juans-MBP:test_mp juan$ python foo.py
List comprehension took 0.14193987846374512 seconds
Pool.map took 0.2513458728790283 seconds
(py37) Juans-MBP:test_mp juan$

この非常に些細な関数の場合、プロセス間通信のコストは、関数をシリアルに計算するコストよりも常に高くなります。したがって、マルチプロセッシングによるメリットは見られません。ただし、それほど重要ではない関数は、マルチプロセッシングからの利益を見ることができます。

これは簡単な例です。乗算する前に、マイクロ秒だけスリープします。

import time
import multiprocessing as mp

def f(x):
    time.sleep(0.000001)
    return x * 100

if __name__ == '__main__':
    data = list(range(1000000))

    start = time.time()
    [f(i) for i in data]
    stop = time.time()
    print(f"List comprehension took {stop - start} seconds")

    start = time.time()
    with mp.Pool(4) as pool:
        result = pool.map(f, data)
    stop = time.time()
    print(f"Pool.map took {stop - start} seconds")

そして今、あなたはプロセスの数に見合った利益を見るでしょう:

(py37) Juans-MBP:test_mp juan$ python foo.py
List comprehension took 13.175776720046997 seconds
Pool.map took 3.1484851837158203 seconds

私のマシンでは、1回の乗算にかかる時間はマイクロ秒(約10ナノ秒よりも桁違いに短いことに注意してください

>>> import timeit
>>> timeit.timeit('100*3', number=int(1e6))*1e-6
1.1292944999993892e-08

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ