web-dev-qa-db-ja.com

Python:マルチコア処理?

Pythonの マルチプロセッシングモジュール について読んでいます。それが何をすることができるかについて私はまだよく理解していません。

クアッドコアプロセッサがあり、1,000,000個の整数のリストがあり、すべての整数の合計が必要だとします。私は単に行うことができます:

list_sum = sum(my_list)

しかし、これは1つのコアにのみ送信します。

マルチプロセッシングモジュールを使用して、配列を分割し、各コアがその部分の合計を取得して値を返すようにして、合計を計算することはできますか?

何かのようなもの:

core1_sum = sum(my_list[0:500000])          #goes to core 1
core2_sum = sum(my_list[500001:1000000])    #goes to core 2
all_core_sum = core1_sum + core2_sum        #core 3 does final computation

任意の助けいただければ幸いです。

25
Nope

はい、複数のスレッドで実行するのと同じように、この合計を複数のプロセスで実行できます。

from multiprocessing import Process, Queue

def do_sum(q,l):
    q.put(sum(l))

def main():
    my_list = range(1000000)

    q = Queue()

    p1 = Process(target=do_sum, args=(q,my_list[:500000]))
    p2 = Process(target=do_sum, args=(q,my_list[500000:]))
    p1.start()
    p2.start()
    r1 = q.get()
    r2 = q.get()
    print r1+r2

if __name__=='__main__':
    main()

ただし、データを前後にコピーする方がすぐに合計するよりもコストがかかるため、複数のプロセスで実行する方が単一のプロセスで実行するよりも遅くなる可能性があります。

32

並行プログラミングの世界へようこそ。

Pythonできることとできないこと)は、2つのことに依存します。

  1. OSができること(できないこと)。ほとんどのOSはコアにプロセスを割り当てます。 4つのコアを使用するには、問題を4つのプロセスに分割する必要があります。これは思ったより簡単です。時々。

  2. 基礎となるCライブラリができること(できないこと)。 CライブラリがOSの機能を公開し、OSがハードウェアの機能を公開する場合は、問題ありません。

問題を複数のプロセス(特にGNU/Linux)に分割するのは簡単です。それをマルチステップパイプラインに分割します。

100万の数値を合計する場合、次のシェルスクリプトを考えてください。架空のsum.py stdinの数値の範囲または数値のリストを合計するプログラム。

(sum.py 0 500000&sum.py 50000 1000000)| sum.py

これには3つの同時プロセスがあります。 2つは多くの数値の合計を行っており、3つ目は2つの数値を合計しています。

GNU/LinuxシェルとOSはすでに並行処理の一部を処理しているので、stdinから読み取り、stdoutに書き込み、大きなジョブの小さな部分を実行するように設計された単純な(非常に単純な)プログラムを設計できます。

シェルにジョブを割り当てる代わりに、 subprocess を使用してパイプラインを構築することで、オーバーヘッドの削減を試みることができます。ただし、シェルがパイプラインを非常に迅速に構築する場合があります。 (Cで直接作成され、OS APIを直接呼び出します。)

20
S.Lott

確かに、例えば:

from multiprocessing import Process, Queue

thelist = range(1000*1000)

def f(q, sublist):
    q.put(sum(sublist))

def main():
    start = 0
    chunk = 500*1000
    queue = Queue()
    NP = 0
    subprocesses = []
    while start < len(thelist):
      p = Process(target=f, args=(queue, thelist[start:start+chunk]))
      NP += 1
      print 'delegated %s:%s to subprocess %s' % (start, start+chunk, NP)
      p.start()
      start += chunk
      subprocesses.append(p)
    total = 0
    for i in range(NP):
      total += queue.get()
    print "total is", total, '=', sum(thelist)
    while subprocesses:
      subprocesses.pop().join()

if __name__ == '__main__':
    main()

結果は:

$ python2.6 mup.py 
delegated 0:500000 to subprocess 1
delegated 500000:1000000 to subprocess 2
total is 499999500000 = 499999500000

この粒度は細かすぎてプロセスを生成する価値がないことに注意してください-全体的な合計タスクは小さく(メインでチェックとして合計を再計算できるのはそのためです;-)、あまりにも多くのデータが(実際、サブプロセスは、作業するサブリストのコピーを取得する必要はありません-インデックスで十分です)。したがって、これはマルチプロセッシングが実際に保証されない「おもちゃの例」です。ただし、さまざまなアーキテクチャ(複数のタスクを受け取るサブプロセスのプールを使用してキューから実行する、データの移動を最小限に抑えるなど)を使用すると、粒度の低いタスクでは実際にパフォーマンスの面でメリットが得られます。

7
Alex Martelli