1. ホーム
  2. python

[解決済み] マルチプロセッシングにおける共有メモリ

2022-03-12 16:26:02

質問

私は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はcopy-on-writeを使っていて、これはデータがコピーされないことを意味しているはずです。一方、オブジェクトにアクセスすると、その参照カウントが変更されます(なぜ、どういう意味なのか、まだよくわかりません)。それでも、オブジェクト全体がコピーされるのでしょうか?

例えば、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 一応補足しておきます。サブプロセスでは l1 , l2l3 またはこれらのリストに含まれるオブジェクト。サブプロセスでは、各サブプロセスでメモリをコピーすることなく、これらのオブジェクトの一部を参照できればよいのです。

解決方法は?

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

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

Pythonのマルチスレッドは(GILのため)CPUバウンドタスクに適していないので、その場合の通常の解決策は、次のようになります。 multiprocessing . しかし、この解決策では、データを明示的に共有する必要があります。 multiprocessing.Value multiprocessing.Array .

通常、プロセス間でデータを共有することは、同期の問題があるため、最良の選択とは言えないかもしれないことに注意してください。以下も参照してください。 Pythonのドキュメント :

上記のように、同時並行プログラミングを行う場合、通常は 共有状態の使用はできるだけ避けた方がよいでしょう。これは 特に、複数のプロセスを使用する場合はそうです。

しかし、どうしても共有データを使用する必要がある場合は、以下のようになります。 マルチプロセシングは、そのためのいくつかの方法を提供します。

あなたの場合 l1 , l2l3 が理解できるような形で multiprocessing (を使用するなどして)。 multiprocessing.Array ) を作成し、それをパラメータとして渡します。
また、書き込みアクセスは必要ないとのことですが、その場合は lock=False さもないと、すべてのアクセスがシリアライズされたままになってしまいます。