web-dev-qa-db-ja.com

マルチプロセッシングの共有メモリ

3つの大きなリストがあります。最初はビット配列(モジュール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(開始するサブプロセスの数に関係なく)を必要とすることを意味しますか?リストがコピーされますか?

[〜#〜] edit [〜#〜]:主題についてもう少し読んだ後、私はまだ混乱しています。一方では、Linuxはコピーオンライトを使用します。つまり、データはコピーされません。一方、オブジェクトにアクセスすると、そのref-countが変更されます(理由とその意味はまだわかりません)。それでも、オブジェクト全体がコピーされますか?

たとえば、次のように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(これらのリストおよびこれらのリスト内のすべてのオブジェクト)を超えたままであることを確信しています。

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

EDIT追加のメモ。サブプロセスは、l1l2l3、またはこれらのリスト内のオブジェクトを変更する必要はありません。サブプロセスは、サブプロセスごとにメモリをコピーせずに、これらのオブジェクトの一部を参照できる必要があるだけです。

56
FableBlaze

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

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

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

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

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

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

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

45
rob

コピーオンライト機能を使用したい場合、データは静的です(子プロセスでは変更されません)-pythonデータが存在するメモリブロックを混乱させないでください。これを簡単に行うには、CまたはC++構造(たとえば、stl)をコンテナとして使用し、python-のときにデータメモリへのポインターを使用する(またはデータmemをコピーする)独自のpythonラッパーレベルオブジェクトは、もしあれば作成されます。これは、ほとんどpythonシンプルさと cython で簡単に行えます。

#pseudo cython 
 cdef class 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):
 return self.data 
 
#python part 
 from foo import FooContainer 
 
 f = FooContainer( "hello world")
 pid = fork()
 if not pid:
 f.get()#この呼び出しは、同じメモリページをwhere 
に読み取ります。#親プロセスは1024文字のself.dataを書き込みました
#とcythonは自動的に新しいpython string 
#オブジェクトを作成し、呼び出し元に戻ります。

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

11
Turnaev Evgeny

Memcachedまたはredisを使用して、それぞれをキーと値のペアとして設定できます{'l1' ...

3
CrabbyPete