7

私は、大規模な(数万から数十万の数値積分)一連の問題に対してかなり重い数値作業を行うコードに取り組んでいます。幸い、これらの統合は驚異的並列であるため、Pool.map()を使用して作業を複数のコアに分割するのは簡単です。

現在、この基本的なワークフローを持つプログラムがあります。

#!/usr/bin/env python
from multiprocessing import Pool
from scipy import *
from my_parser import parse_numpy_array
from my_project import heavy_computation

#X is a global multidimensional numpy array
X = parse_numpy_array("input.dat")
param_1 = 0.0168
param_2 = 1.505

def do_work(arg):
  return heavy_computation(X, param_1, param_2, arg)

if __name__=='__main__':
  pool = Pool()
  arglist = linspace(0.0,1.0,100)
  results = Pool.map(do_work,arglist)
  #save results in a .npy file for analysis
  save("Results", [X,results])

X、param_1、およびparam_2はハードコーディングされており、プール内の各プロセスに対してまったく同じ方法で初期化されるため、これはすべて正常に機能します。コードが機能するようになったので、ファイル名、param_1、およびparam_2がハードコーディングされるのではなく、実行時にユーザーによって入力されるようにします。

注意すべきことの1つは、作業が行われている間、X、param_1、およびparam_2は変更されないということです。私はそれらを変更しないので、プログラムの開始時に次のようなことを行うことができます。

import sys
X = parse_numpy_array(sys.argv[1])
param_1 = float(sys.argv[2])
param_2 = float(sys.argv[3])

これでうまくいきますが、このコードのほとんどのユーザーはWindowsマシンからコードを実行しているので、コマンドライン引数のルートには行きたくありません。

私が本当にやりたいのは次のようなものです。

X, param_1, param_2 = None, None, None

def init(x,p1, p2)
  X = x
  param_1 = p1
  param_2 = p2

if __name__=='__main__':
  filename = raw_input("Filename> ")
  param_1 = float(raw_input("Parameter 1: "))
  param_2 = float(raw_input("Parameter 2: "))
  X = parse_numpy_array(filename)
  pool = Pool(initializer = init, initargs = (X, param_1, param_2,))
  arglist = linspace(0.0,1.0,100)
  results = Pool.map(do_work,arglist)
  #save results in a .npy file for analysis
  save("Results", [X,results])

ただし、もちろん、これは失敗し、pool.map呼び出しが発生すると、X / param_1/param_2はすべてNoneになります。私はマルチプロセッシングにかなり慣れていないので、イニシャライザーの呼び出しが失敗する理由がわかりません。私がやりたいことをする方法はありますか?これを完全に回避するためのより良い方法はありますか?共有データの使用も検討しましたが、ドキュメントの理解から、numpy配列を含まないctypeでのみ機能します。これに関する助けをいただければ幸いです。

4

2 に答える 2

5

私も同様の問題を抱えていました。私の解決策を読みたいだけなら、いくつかの行をスキップしてください:)私はしなければなりませんでした:

  • numpy.arrayを、その異なる部分で動作しているスレッド間で共有し、...
  • 複数の引数を持つ関数をPool.mapに渡します。

きがついた:

  • numpy.arrayのデータは正しく読み取られましたが...
  • 永続化されていないnumpy.arrayの変更
  • Pool.mapでラムダ関数の処理に問題があったため、私にはわかりました(この点が明確でない場合は、無視してください)

私の解決策は次のとおりです。

  • ターゲット関数を引数のみのリストにする
  • numpy.arrayに直接書き込もうとするのではなく、ターゲット関数が変更されたデータを返すようにします

do_work関数はすでに計算されたデータを返すことを理解しているので、to_workを変更して、引数としてリスト(X、param_1、param_2、およびargを含む)を受け入れ、渡す前にこの形式でターゲット関数への入力をパックする必要があります。それをPool.mapに追加します。

実装例は次のとおりです。

def do_work2(args):
    X,param_1,param_2,arg = args
    return heavy_computation(X, param_1, param_2, arg)

ここで、do_work関数への入力を呼び出す前にパックする必要があります。あなたのメインは次のようになります:

if __name__=='__main__':
   filename = raw_input("Filename> ")
   param_1 = float(raw_input("Parameter 1: "))
   param_2 = float(raw_input("Parameter 2: "))
   X = parse_numpy_array(filename)
   # now you pack the input arguments
   arglist = [[X,param1,param2,n] for n in linspace(0.0,1.0,100)]
   # consider that you're not making 100 copies of X here. You're just passing a reference to it
   results = Pool.map(do_work2,arglist)
   #save results in a .npy file for analysis
   save("Results", [X,results])
于 2012-09-23T06:28:29.067 に答える
-2

X最後のアイデアを機能させるには、ifステートメント内で変更する前に、globalキーワードを使用して、、、、param_1およびグローバル変数を作成するだけでよいと思います。param_2したがって、以下を追加します。

global X
global param_1
global param_2

直後if __name__ == '__main__'

于 2012-08-15T02:55:38.390 に答える