2013-04-15 13 views
11

これは、私がPythonでやっているいくつかの統計分析でパフォーマンスを向上させようとした私の第3の最終的な質問です。私は2つのバージョンのコード(1つのコアと2つのマルチプロセッシング)を持っていますが、私はコードがかなりの数のバイナリ文字列を解凍/解凍することを期待しているので、複数のコアを使用してパフォーマンスを得ることを期待していました。コア。Pythonマルチプロセッシングの性能

誰でも私が観察したことについての可能な説明があるかどうか疑問に思っています(詳細は、4月16日の更新までスクロールしてください)

プログラムの重要な部分は、関数numpy_array(さらに以下ペーストビンを介してアクセス可能なフルコード)(マルチにおける+デコード)、以下のコードである:

def numpy_array(data, peaks): 
    rt_counter=0 
    for x in peaks: 
     if rt_counter %(len(peaks)/20) == 0: 
      update_progress() 
     peak_counter=0 
     data_buff=base64.b64decode(x) 
     buff_size=len(data_buff)/4 
     unpack_format=">%dL" % buff_size 
     index=0 
     for y in struct.unpack(unpack_format,data_buff): 
      buff1=struct.pack("I",y) 
      buff2=struct.unpack("f",buff1)[0] 
      if (index % 2 == 0): 
       data[rt_counter][1][peak_counter][0]=float(buff2) 
      else: 
       data[rt_counter][1][peak_counter][1]=float(buff2) 
       peak_counter+=1 
      index+=1 
     rt_counter+=1 

マルチバージョンがセットでこれを行います機能は、私が表示されますキー2の下:

def tonumpyarray(mp_arr): 
    return np.frombuffer(mp_arr.get_obj()) 

def numpy_array(shared_arr,peaks): 
    processors=mp.cpu_count() 
    with contextlib.closing(mp.Pool(processes=processors, 
            initializer=pool_init, 
            initargs=(shared_arr,))) as pool: 
     chunk_size=int(len(peaks)/processors) 
     map_parameters=[] 
     for i in range(processors): 
      counter = i*chunk_size 
      chunk=peaks[i*chunk_size:(i+1)*chunk_size] 
      map_parameters.append((chunk, counter)) 
     pool.map(decode,map_parameters) 

def decode ((chunk, counter)): 
    data=tonumpyarray(shared_arr).view(
     [('f0','<f4'), ('f1','<f4',(250000,2))]) 
    for x in chunk: 
     peak_counter=0 
     data_buff=base64.b64decode(x) 
     buff_size=len(data_buff)/4 
     unpack_format=">%dL" % buff_size 
     index=0 
     for y in struct.unpack(unpack_format,data_buff): 
      buff1=struct.pack("I",y) 
      buff2=struct.unpack("f",buff1)[0] 
      #with shared_arr.get_lock(): 
      if (index % 2 == 0): 
       data[counter][1][peak_counter][0]=float(buff2) 
      else: 
       data[counter][1][peak_counter][1]=float(buff2) 
       peak_counter+=1 
      index+=1 
     counter+=1 

完全なプログラムコードは、これらのペーストビンリンクを介してアクセスすることができます

Pastebin for single core version

Pastebin for multiprocessing version

私が時点ごとに239回の時点と〜180Kの測定のペアを含むファイルを観察していますパフォーマンスが~~マルチプロセッシングのための3.5シングルコアの2.5メートルとあります。

PS:(paralellizationにしようと、これまでの私の最初の)前の2つの質問:

  1. Python multi-processing
  2. Making my NumPy array shared across processes

- 4月16日 -

私はtで私のプログラムをプロファイリングしています彼cProfileライブラリ(すべてを減速している1つのステップがあることを示しており、__main__cProfile.run('main()')を持つ:私の理解へ

ncalls tottime percall cumtime percall filename:lineno(function) 
23 85.859 3.733 85.859 3.733 {method 'acquire' of 'thread.lock' objects} 

私はここで理解していない事がthread.lockオブジェクトがthreadingで使用されていることです( )、マルチコアでは使用するべきではありません。なぜなら、各コアは単一のスレッドを実行する必要があります(独自のロック機構を持つ以外に)。

+1

この質問の前の質問へのリンクを共有することはできますか?重要であると思う機能を質問自体に貼り付けてください – 0x90

+0

オフコース、質問を編集させてください。 –

+0

それはGILと何か関係があります。このプレゼンテーションをチェックしてください:http://www.youtube.com/watch?v=Obt-vMVdM8s – alexhb

答えて

2

共有データは、同期化による減速の既知のケースです。

プロセス間でデータを分割したり、各プロセスに独立したコピーを与えることはできますか?その後、すべての計算が完了するまで、プロセスは何かを同期させる必要はありません。

次に、マスタプロセスがすべてのワーカープロセッサの出力を1つのコヒーレントセットに結合させるようにします。

このアプローチでは余分なRAMが必要になるかもしれませんが、最近はRAMが安いです。

私が尋ねると、私はスレッドロック取得ごとに3700ミリ秒も困惑しています。 OTOHのプロファイリングは、このような特別な呼び出しについて間違っているかもしれません。

+0

私はおそらくデータアレイをn個のコピーに分割することができましたが、なぜ、どのようにプロセス全体が長くかかるかを理解したいと思います。別の方法でプロファイリングすることをお勧めしますか? –

0

あなたのペーストビンは空です。

問題は、マルチプロセッシングでは、新しいPythonプロセスを生成する代わりに、使用可能な場合にforkを使用することです。フォークされたプロセスは同じenvを共有します(例:ファイル記述子)。その中にロックがあるかもしれません。ここで

が、そのことについていくつかの不満です:Multiprocessing or os.fork, os.exec?

0

は限りあなたの質問の最後の部分として、Pythonのドキュメントは基本的にmultiprocessing.lockがthreading.lockのクローンであることを言います。ロックがすでに取得されている場合は、ロックが解除されるまでブロックされますので、ロックの取得コールには長い時間がかかります。これは、コード内のように、複数のプロセスが同じデータにアクセスするために競合している場合に問題になります。あなたのペーストビンは見ることができないので、何が起こっているのか正確に推測することができますが、プロセスは長時間ロックを取得しています。フリーのCPU時間。これはGILの影響を受けるべきではありません。なぜならマルチプロセッシングされたアプリケーションではなくマルチスレッドのアプリケーションのみを制限するべきだからです。では、これを修正する方法は?私の推測では、あなたの共有配列を保護する何らかのロックを持っていることです。ロックされているプロセスは、比較的長い時間がかかる集中的な計算をしているので、他のプロセスへのアクセスが禁止されています。コール。十分なRAMがあると仮定すると、アレイの複数のコピーを各プロセスのアドレス空間に格納することを示唆する答えを強く支持します。しかし、地図を介して大きなデータ構造を渡すと、ピッキングとデキッキングが必要になるため、予期しないボトルネックが発生することに注意してください。

関連する問題