60GBのSciPyアレイ(マトリックス)を持っています。5個以上のmultiprocessing
Process
オブジェクト間で共有する必要があります。私はnumpy-sharedmemを見て、SciPyリストで この議論 を読みました。 numpy-sharedmem
とmultiprocessing.RawArray()
を使用し、NumPy dtype
sをctype
sにマッピングする2つのアプローチがあるようです。現在、numpy-sharedmem
が方法のように見えますが、良い参考例をまだ見ていません。配列(実際には行列)は読み取り専用になるため、ロックは必要ありません。今、そのサイズのために、私はコピーを避けたいです。 のように聞こえます正しい方法は、onlyのコピーを作成することですsharedmem
配列として配列し、Process
オブジェクトに渡しますか?いくつかの具体的な質問:
Sharedmemハンドルを実際にsub-Process()
esに渡す最良の方法は何ですか? 1つの配列を渡すためだけにキューが必要ですか?パイプの方が良いでしょうか? Process()
サブクラスのinitに引数として渡すことはできますか(ピクルスされていると想定しています)。
上記でリンクしたディスカッションで、numpy-sharedmem
が64ビットセーフではないという言及がありますか?私は間違いなく、32ビットでアドレス指定できない構造を使用しています。
RawArray()
アプローチにはトレードオフがありますか?遅くて、バグが多い?
Numpy-sharedmemメソッドのctypeからdtypeへのマッピングは必要ですか?
これを行うOpenSourceコードの例はありますか?私は非常に実践的な知識を持っているので、見栄えの良い例がなければこれを機能させるのは困難です。
これを他の人にわかりやすくするために提供できる追加情報がある場合は、コメントして追加してください。ありがとう!
これは、Ubuntu LinuxおよびMaybeMac OSで実行する必要がありますが、移植性は大きな問題ではありません。
@Velimir Mlakerがすばらしい答えをくれました。少しコメントと小さな例を追加できると思いました。
(sharedmemに関する多くのドキュメントを見つけることができませんでした-これらは私自身の実験の結果です。)
target
にargs
およびProcess
引数を使用できます。これは、グローバル変数を使用するよりも潜在的に優れています。#!/usr/bin/env python
from multiprocessing import Process
import sharedmem
import numpy
def do_work(data, start):
data[start] = 0;
def split_work(num):
n = 20
width = n/num
shared = sharedmem.empty(n)
shared[:] = numpy.random.Rand(1, n)[0]
print "values are %s" % shared
processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)]
for p in processes:
p.start()
for p in processes:
p.join()
print "values are %s" % shared
print "type is %s" % type(shared[0])
if __== '__main__':
split_work(4)
values are [ 0.81397784 0.59667692 0.10761908 0.6736734 0.46349645 0.98340718
0.44056863 0.10701816 0.67167752 0.29158274 0.22242552 0.14273156
0.34912309 0.43812636 0.58484507 0.81697513 0.57758441 0.4284959
0.7292129 0.06063283]
values are [ 0. 0.59667692 0.10761908 0.6736734 0.46349645 0.
0.44056863 0.10701816 0.67167752 0.29158274 0. 0.14273156
0.34912309 0.43812636 0.58484507 0. 0.57758441 0.4284959
0.7292129 0.06063283]
type is <type 'numpy.float64'>
この 関連する質問 は役に立つかもしれません。
Linux(またはPOSIX準拠のシステム)を使用している場合、この配列をグローバル変数として定義できます。 Linuxでは、multiprocessing
が新しい子プロセスを開始するときにfork()
を使用しています。新しく生成された子プロセスは、変更しない限り自動的に親とメモリを共有します( copy-on-write メカニズム)。
「配列(実際にはマトリックス)は読み取り専用であるため、いかなる種類のロックも必要ありません」と言っているため、この動作を利用することは非常にシンプルでありながら非常に効率的なアプローチです。すべての子プロセスがアクセスしますこの大きなnumpy配列を読み取るときに、物理メモリ内の同じデータ。
配列をProcess()
コンストラクターに渡さないでください。これは、multiprocessing
からpickle
に子にデータを指示します。これは、非常に非効率的または不可能です。 Linuxでは、fork()
の直後に、子は同じ物理メモリを使用する親の正確なコピーであるため、必要なことはPython変数 'を含む行列は、Process()
に渡すtarget
関数内からアクセスできます。これは通常、 'global'変数で実現できます。
サンプルコード:
_from multiprocessing import Process
from numpy import random
global_array = random.random(10**4)
def child():
print sum(global_array)
def main():
processes = [Process(target=child) for _ in xrange(10)]
for p in processes:
p.start()
for p in processes:
p.join()
if __== "__main__":
main()
_
Windows-fork()
をサポートしない-multiprocessing
はwin32 API呼び出しCreateProcess
を使用しています。任意の実行可能ファイルからまったく新しいプロセスを作成します。そのため、Windowsでは、親の実行時に作成されたデータが必要な場合、子にデータをピクルするためにrequiredが必要です。
私が書いた小さなコードに興味があるかもしれません: github.com/vmlaker/benchmark-sharedmem
対象のファイルは_main.py
_のみです。 numpy-sharedmem のベンチマークです。コードは、配列(numpy
またはsharedmem
のいずれか)を、Pipeを介して生成されたプロセスに渡すだけです。ワーカーはデータに対してsum()
を呼び出すだけです。 2つの実装間のデータ通信時間の比較にのみ興味がありました。
また、より複雑な別のコードを作成しました: github.com/vmlaker/sherlock 。
ここでは、OpenCVを使用したリアルタイムの画像処理に numpy-sharedmem モジュールを使用します。画像は、OpenCVの新しい_cv2
_ APIに従って、NumPy配列です。実際にその参照である画像は、 _multiprocessing.Manager
_ から作成されたディクショナリオブジェクトを介してプロセス間で共有されます(キューまたはパイプを使用するのとは対照的です)。プレーンなNumPy配列。
パイプとキュー:
私の経験では、IPC Pipeの方がキューより高速です。キューは複数のプロデューサー/コンシューマーに対して安全にするためにロックを追加するため、それは理にかなっています。パイプはありません。 2つのプロセスが行き来しており、Pipeを使用しても安全です。または、ドキュメントを読むと:
...パイプの異なる端を同時に使用するプロセスによる破損のリスクはありません。
sharedmem
安全性:
sharedmem
モジュールの主な問題は、プログラムの異常終了時にメモリリークが発生する可能性があることです。これは、長い議論 ここ で説明されています。 2011年4月10日、Sturlaはメモリリークの修正について言及していますが、それ以来、GitHubでSturla Molden独自のリポジトリ( github.com/sturlamolden/sharedmem-numpy )とクリス・リー・メッサーのBitbucket( bitbucket.org/cleemesser/numpy-sharedmem )。
配列がこれほど大きい場合は、numpy.memmap
を使用できます。たとえば、'test.array'
などのディスクに配列を保存している場合、「書き込み」モードでも同時プロセスを使用してそのデータにアクセスできますが、「読み取り」モードのみが必要なため、ケースは簡単です。
配列の作成:
a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))
その後、通常の配列と同じ方法でこの配列を埋めることができます。例えば:
a[:10,:100]=1.
a[10:,100:]=2.
変数a
を削除すると、データはディスクに保存されます。
後で、test.array
のデータにアクセスする複数のプロセスを使用できます。
# read-only mode
b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000))
# read and writing mode
c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))
関連する回答:
pyro のドキュメントをご覧になると便利です。タスクを適切にパーティション分割できる場合は、それを使用して、同じマシンの異なるコアや異なるコアで異なるセクションを実行できます。機械。