92

3 つの大きなリストがあります。1 つは bitarray (モジュール bitarray 0.8.0) を含み、残りの 2 つは整数の配列を含みます。

l1=[bitarray 1, bitarray 2, ... ,bitarray n]
l2=[array 1, array 2, ... , array n]
l3=[array 1, array 2, ... , array n]

これらのデータ構造は、かなりの量の RAM (合計 16GB まで) を消費します。

次を使用して12のサブプロセスを開始した場合:

multiprocessing.Process(target=someFunction, args=(l1,l2,l3))

これは、サブプロセスごとに l1、l2、および l3 がコピーされるということですか、それともサブプロセスがこれらのリストを共有するということですか? または、より直接的に言えば、16GB または 192GB の RAM のどちらを使用しますか?

someFunction は、これらのリストからいくつかの値を読み取り、読み取った値に基づいていくつかの計算を実行します。結果は親プロセスに返されます。リスト l1、l2、および l3 は someFunction によって変更されません。

したがって、サブプロセスはこれらの巨大なリストを必要とせず、コピーせず、代わりに親と共有するだけであると想定します。Linuxでのコピーオンライトアプローチにより、プログラムが16GBのRAMを使用することを意味します(開始するサブプロセスの数に関係なく)? 私は正しいですか、それともリストがコピーされる原因となる何かが欠けていますか?

編集:この件についてもう少し読んだ後、私はまだ混乱しています。一方では、Linux はコピー オン ライトを使用します。これは、データがコピーされないことを意味します。一方、オブジェクトにアクセスすると、その参照カウントが変更されます(理由と意味はまだわかりません)。それでも、オブジェクト全体がコピーされますか?

たとえば、someFunction を次のように定義するとします。

def someFunction(list1, list2, list3):
    i=random.randint(0,99999)
    print list1[i], list2[i], list3[i]

この関数を使用すると、サブプロセスごとに l1、l2、および l3 が完全にコピーされることになりますか?

これを確認する方法はありますか?

EDIT2もう少し読んで、サブプロセスの実行中にシステムの総メモリ使用量を監視した後、サブプロセスごとにオブジェクト全体が実際にコピーされているようです。そして、参照カウントが原因のようです。

私のプログラムでは、l1、l2、および l3 の参照カウントは実際には必要ありません。これは、親プロセスが終了するまで、l1、l2、および l3 がメモリに (変更されずに) 保持されるためです。それまでは、これらのリストで使用されているメモリを解放する必要はありません。実際、プログラムが終了するまで、(これらのリストおよびこれらのリスト内のすべてのオブジェクトに対して) 参照カウントが 0 を超えたままになることは確かです。

さて、問題は、オブジェクトが各サブプロセスにコピーされないようにするにはどうすればよいですか? これらのリストとこれらのリスト内の各オブジェクトの参照カウントを無効にすることはできますか?

EDIT3 追記です。サブプロセスは、これらのリスト内の やオブジェクトl1l2変更する必要はありません。l3サブプロセスは、サブプロセスごとにメモリがコピーされることなく、これらのオブジェクトの一部を参照できる必要があるだけです。

4

5 に答える 5

70

一般的に、同じデータを共有する方法は2つあります。

  • マルチスレッド
  • 共有メモリ

Pythonのマルチスレッドは(GILのために)CPUにバインドされたタスクには適していないため、その場合の通常の解決策は続行することmultiprocessingです。ただし、このソリューションでは、とを使用してデータを明示的に共有する必要がありmultiprocessing.Valueますmultiprocessing.Array

すべての同期の問題があるため、通常、プロセス間でデータを共有することは最善の選択ではない場合があることに注意してください。アクターがメッセージを交換することを含むアプローチは、通常、より良い選択と見なされます。Pythonのドキュメントも参照してください:

上記のように、並行プログラミングを行うときは、通常、共有状態の使用を可能な限り避けるのが最善です。これは、複数のプロセスを使用する場合に特に当てはまります。

ただし、実際に共有データを使用する必要がある場合は、マルチプロセッシングによっていくつかの方法が提供されます。

あなたの場合、をラップしl1、何らかの方法で(たとえば、を使用して)理解できるようにしてから、それらをパラメーターとして渡す必要があります。 また、書き込みアクセスは必要ないと述べたように、オブジェクトの作成中にパスする必要があります。そうしないと、すべてのアクセスが引き続きシリアル化されます。l2l3multiprocessingmultiprocessing.Array
lock=False

于 2013-01-03T08:50:13.307 に答える
11

コピーオンライト機能を利用したいが、データが静的である(子プロセスで変更されていない)場合は、Pythonがデータのあるメモリブロックを混乱させないようにする必要があります。これは、CまたはC ++構造(たとえばstl)をコンテナーとして使用して簡単に行うことができ、Pythonレベルのオブジェクトが作成されるときにデータメモリへのポインターを使用する(またはデータメモリをコピーする)独自のPythonラッパーを提供します。 。これはすべて、ほぼpythonの単純さとcythonの構文で非常に簡単に実行できます。

#疑似cython
cdefクラスFooContainer:
   cdef char * data
   def __cinit __(self、char * foo_value):
       self.data = malloc(1024、sizeof(char))
       memcpy(self.data、foo_value、min(1024、len(foo_value)))
   
   def get(self):
       self.dataを返します

#Pythonパート
fooからimportFooContainer

f = FooContainer( "hello world")
pid = fork()
pidでない場合:
   f.get()#この呼び出しは、同じメモリページを次の場所に読み取ります
           #親プロセスは1024文字のself.dataを書き込みました
           #とcythonは自動的に新しいPython文字列を作成します
           #それからオブジェクトし、発信者に戻る

上記の擬似コードは正しく記述されていません。使用しないでください。あなたの場合、self.dataの代わりにCまたはC++コンテナを使用する必要があります。

于 2013-01-03T19:41:34.547 に答える