web-dev-qa-db-ja.com

Pythonでリスト内包計算を並列化する方法は?

リスト内包表記とマップ計算はどちらも、少なくとも理論的には比較的簡単に並列化できます。リスト内包表記内の各計算は、他のすべての要素の計算とは無関係に実行できます。たとえば、式

[ x*x for x in range(1000) ]

各x * x計算は(少なくとも理論的には)並行して実行できます。

私の質問は:Pythonモジュール/ Python実装/ Pythonプログラミングトリックはありますか?リスト理解計算を並列化するためのプログラミングトリック(すべての16/32/...計算をコンピューターグリッドまたはクラウドに分散しますか?)

44
phynfo

ケンが言ったように、それはできませんが、2.6の multiprocessing モジュールを使用すると、計算を並列化するのは非常に簡単です。

import multiprocessing

try:
    cpus = multiprocessing.cpu_count()
except NotImplementedError:
    cpus = 2   # arbitrary default


def square(n):
    return n * n

pool = multiprocessing.Pool(processes=cpus)
print(pool.map(square, range(1000)))

documentation には、Managerを使用してこれを行う方法を示す例もあります。これにより、分散計算も可能になります。

34

リスト内包の自動並列化について

私見、効果的リスト内包の自動パラレリゼーションは、追加情報(OpenMPのディレクティブを使用して提供されるものなど)、またはそれを含む式に限定しないと不可能です組み込みのタイプ/メソッドのみ。

各リスト項目で実行される処理に副作用がないことが保証されていない限り、順序が正しくないと、結果が無効になる(または少なくとも異なる)可能性があります。

_# Artificial example
counter = 0

def g(x): # func with side-effect
    global counter
    counter = counter + 1
    return x + counter

vals = [g(i) for i in range(100)] # diff result when not done in order
_

タスクの分配の問題もあります。問題の空間はどのように分解すべきですか?

各要素の処理がタスク(〜タスクファーム)を形成する場合、それぞれが簡単な計算を伴う要素が多数ある場合、タスクの管理のオーバーヘッドにより、並列化によるパフォーマンスの向上が失われます。

問題の空間が利用可能なプロセス間で均等に分割されるデータ分解アプローチを取ることもできます。

リスト内包表記がジェネレーターでも機能するという事実は、これを少しトリッキーにしますが、事前反復のオーバーヘッドが許容できる場合、これはおそらくショーストッパーではありません。もちろん、後続の項目が時期尚早に反復される場合、結果が変わる可能性のある副作用を持つジェネレーターの可能性もあります。可能性は非常に低いですが、可能です。

より大きな問題は、プロセス間の負荷の不均衡です。各要素の処理に同じ時間がかかるという保証はありません。そのため、静的にパーティション分割されたデータにより、1つのプロセスがアイドル時間中にほとんどの作業を実行する可能性があります。

リストをより小さなチャンクに分割し、各子プロセスが利用可能になるときにそれらを渡すことは、適切な妥協ですが、チャンクサイズの適切な選択はアプリケーションに依存するため、ユーザーからの詳細情報がないと実行できません。

代替案

他のいくつかの回答で述べたように、1つの要件に応じて多くのアプローチと 並列計算モジュール/フレームワーク から選択できます。

並列処理にMPIを使用した経験がなく、Python(Cで)のみを使用した場合、私は(ただし、クイックスキャン、 マルチプロセッシングjugpp および pyro 目立つ)。

リスト内包表記にできるだけ近づけることが要件である場合、 jug が最も一致するようです。 tutorial から、複数のインスタンスにタスクを分散することは次のように簡単です。

_from jug.task import Task
from yourmodule import process_data
tasks = [Task(process_data,infile) for infile in glob('*.dat')]
_

これはmultiprocessing.Pool.map()に似ていますが、jugはプロセスの同期と中間結果(redis、ファイルシステム、メモリ内)の保存に異なるバックエンドを使用できます。つまり、プロセスは集まる。

9
Shawn Chin

共有メモリの並列処理については、 joblib をお勧めします。

from joblib import delayed, Parallel

def square(x): return x*x
values = Parallel(n_jobs=NUM_CPUS)(delayed(square)(x) for x in range(1000))
6
Fred Foo

新しい3.2 concurrent.futures パッケージのfutures.{Thread,Process}PoolExecutor.map(func, *iterables, timeout=None)関数とfutures.as_completed(future_instances, timeout=None)関数を使用すると役立つ場合があります。

2.6+ backport としても利用できます。

5
Georges Martin

いいえ、リスト内包自体は一種のC最適化マクロであるためです。それを引き出して並列化すると、それはリストの内包ではなく、古き良き昔ながらの MapReduce になります。

しかし、例を簡単に並列化できます。以下は、Pythonの並列化ライブラリでMapReduceを使用するための優れたチュートリアルです。

http://mikecvet.wordpress.com/2010/07/02/parallel-mapreduce-in-python/

3
Ken Kinder

上記の回答が指摘するように、これを実際に自動的に行うのはかなり困難です。次に、問題は、実際にはどのようにして最も簡単な方法でそれを行うかだと思います。理想的には、ソリューションでは「コアの数」などを知る必要はありません。あなたが望むかもしれないもう一つのプロパティは、単一の読み取り可能な行でリストの理解をまだできるようにすることです。

与えられた回答のいくつかはすでにこのようなNiceプロパティを持っているようですが、別の代替手段はRay( docs )です。これは、並列Pythonを作成するためのフレームワークです。レイでは、次のようにします。

import ray

# Start Ray. This creates some processes that can do work in parallel.
ray.init()

# Add this line to signify that the function can be run in parallel (as a
# "task"). Ray will load-balance different `square` tasks automatically.
@ray.remote
def square(x):
    return x * x

# Create some parallel work using a list comprehension, then block until the
# results are ready with `ray.get`.
ray.get([square.remote(x) for x in range(1000)])
2
Stephanie Wang

Pythonの並列パッケージの包括的なリストはここにあります:

http://wiki.python.org/moin/ParallelProcessing

リスト内包構造の分割を直接処理できるものがあるかどうかはわかりませんが、同じ問題をリスト内包以外の方法で定式化して、複数の異なるプロセッサに簡単に分岐できるようにするのは簡単なはずです。私はクラウドコンピューティングの並列化に詳しくありませんが、マルチコアマシンやクラスターでmpi4pyを使用してある程度成功しました。考慮しなければならない最大の問題は、通信のオーバーヘッドが問題の並列化から得られるすべての利益を殺すかどうかです。

編集:以下も興味深いかもしれません:

http://www.mblondel.org/journal/2009/11/27/easy-parallelization-with-data-decomposition/

1
JoshAdel

リスト内包AFAIK内ではありません。

あなたは確かに伝統的なforループとマルチプロセッシング/スレッド化モジュールでそれを行うことができます。

1
mluebke