質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
並列処理

複数の計算が同時に実行される手法

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

6391閲覧

Multiprocessingの計算順序の保存方法[Python, 並列計算, ]

fushiming

総合スコア9

並列処理

複数の計算が同時に実行される手法

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/04/08 16:29

編集2019/04/08 16:31

下記のようなコードを実行すると、0,2,4,6,8がシャッフルされて出力されます。

可能なら計算を実行した順番通り[0,2,4,6,8]が
固定で出てくるようにしたいのですが、いい方法はあるでしょうか?

(備考)
・実際の配列は時系列の3次元空間データ(つまり4次元)を時系列順に複数に分割して処理している
・上のせいで計算終了時間にズレが生じて、最後に結合した時に時間がしばしばずれる
・calc関数の多分pickle化できないので、Poolではできないと思われる
・普通に直列計算すると1回数日程度かかり、今後相当な回数実行する予定

python

1def calc(queue, p, proc, x=[i for i in range(5)]): 2 #計算を分割するための関数 3 #配列をproc個に分割したもののうち前から数えてp番目の計算を行っている 4 5 import random 6 L = len(x) 7 8 #抜き出す部分の指定 9 ini = L * p // proc 10 fin = L * (p+1) // proc 11 if fin > L: 12 fin=L 13 14  #計算部分 15 tmp = [x[i]*2 for i in range(ini,fin)] 16 time.sleep(random.randint(1,10)) 17 queue.put(tmp) 18 19 return queue 20 21def _parallel(func, proc): 22 import multiprocessing as mp 23 queue = mp.Queue() 24 #計算を投げている部分 25 #下の実行順通りに出力させたい 26 ps = [ mp.Process(target=func, args=(queue, i, proc)) for i in range(proc)] 27 for p in ps: 28 p.start() 29 return queue 30 31#実行部分 32proc=8 33queue=_parallel(calc, proc) 34value=[] 35for l in range(proc): 36 tmp = queue.get() 37 for i in tmp: 38 value.append(i) 39#このままだと0,2,4,6,8がランダムに並んで出力される 40#計算終了時間が正確にわからない状態で[0,2,4,6,8]で確実に出力されるようにしたい。 41print(value)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2019/04/08 18:26

計算時間はsleepで眠ってた時間の総数より短くなりましたか?
fushiming

2019/04/09 02:04

短くなりました。 並列処理はちゃんとできていると思います。
guest

回答2

0

ベストアンサー

どのような順番に結果が返されるのか制御しようとすると並列処理の意味がなくなってしまいかねません。素朴に考えて「このプロセスの結果が何番目のものか」を表す情報を呼び出し元へ返せばよいと思います。
呼び出し元では計算結果に付加されている「データの順序番号」を元に全ての結果が得られた後に順序番号でソートすればよいという考え方です。

Python

1def calc(queue, p, x): 2 3 import random 4 import time 5 6 tmp = x 7 time.sleep(random.randint(1, 10) * 0.1) 8 result = [x * 2 for x in tmp] 9 queue.put((p, result)) 10 11 # return queue 12 13 14def _parallel(func, proc, input_data): 15 import multiprocessing as mp 16 import math 17 # CPUバウンドな並列処理の場合搭載プロセッサー数より大きな分割数にしても効果は望めない 18 proc = min(proc, mp.cpu_count()) 19 # 要素数より大きな分割数は意味がない 20 gap = max(1, len(input_data) // proc) 21 proc = math.ceil(len(input_data) / gap) 22 print(f"proc={proc}, gap={gap}") 23 # 計算を投げている部分 24 queue = mp.Queue() 25 for i in range(0, len(input_data), gap): 26 # 計算対象のデータは起動元で分割しておくべきでは? 27 x = input_data[i * gap: (i + 1) * gap] 28 p = mp.Process(target=func, args=(queue, i, x)) 29 p.start() 30 return proc, queue 31 32 33# Unix系OSでは__main__モジュールかどうかの判定は不要。 34# しかしWindowsにおいては必須。 35# なぜならforkシステムコールがないため子プロセスは親プロセスと同じように 36# プログラムの最初から実行が開始されるため子でも親と同じ処理が動き出してしまう。 37if __name__ == '__main__': 38 # 実行部分 39 proc = 8 40 input_data = list(range(5)) 41 proc, queue = _parallel(calc, proc, input_data) 42 values = sorted((queue.get() for _ in range(proc)), key=lambda r: r[0]) 43 from functools import reduce 44 from operator import add 45 values = reduce(add, map(lambda a: a[1], values), []) 46 47 print(values)

上記ではご質問の本質に関係ない部分も変更しています。

  • CPUバウンドな並列処理の場合はプロセッサー数以上の分割に意味がない

ここではプロセッサー数を子プロセス数の上限としてみました

  • 処理対象のデータ数以上にプロセスを分割しても意味がない

ここではデータ数を子プロセス数の上限としてみました

  • Linux系だけでなくWindowsにも配慮した

単に動作確認をWindowsでやったからです。Windowsにはforkシステムコールがないため子プロセスは親と同じメインモジュールを最初からロード&実行しようとします。そのため上に書いたようにモジュール名__main__の判定をしないと子プロセスでも親と同じ処理が始まってしまいます。また同じ理由で子プロセスへの引数は親からプロセス間通信を用いて伝えられると思うので子プロセスに担当部分以外のデータを渡すことはオーバーヘッドになると考えました。そこでデータの分割は親プロセスで行い、子プロセスには分割済みのデータのみを渡すようにしました。

Linux: Python 3.6.4
Windows: Python 3.7.0

投稿2019/04/09 00:46

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fushiming

2019/04/09 02:46

ありがとうございます!サンプルコードまでいただき大変助かりました!! また、質問させていただいた部分以外にも様々な指摘をいただき、大変参考になりました。 当方はMacOSを主に使っていたので、考えていなかったのですが、Windowでは__main__の判定が必須なんですね。。。Windowsユーザーにコード渡す時は十分留意したいと思います。
guest

0

https://qiita.com/t_okkan/items/4127a87177ed2b2db148

並列化ルーチンに入る時インデックスを渡して、値を入れる場所を無理やり固定することもできます。

投稿2019/04/09 00:15

mkgrei

総合スコア8560

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fushiming

2019/04/09 02:09

ありがとうございます!確かに並列化前にインデックス渡しておけば確実ですね!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問