123

マルチプロセッシングによって生成された子プロセスは、プログラムで以前に作成されたオブジェクトを共有しますか?

私は次の設定をしています:

do_some_processing(filename):
    for line in file(filename):
        if line.split(',')[0] in big_lookup_object:
            # something here

if __name__ == '__main__':
    big_lookup_object = marshal.load('file.bin')
    pool = Pool(processes=4)
    print pool.map(do_some_processing, glob.glob('*.data'))

大きなオブジェクトをメモリにロードし、その大きなオブジェクトを利用する必要があるワーカーのプールを作成しています。大きなオブジェクトは読み取り専用でアクセスされるため、プロセス間で変更を渡す必要はありません。

私の質問は、unix/c でプロセスを生成した場合のように、ビッグ オブジェクトが共有メモリにロードされるのか、それとも各プロセスがビッグ オブジェクトの独自のコピーをロードするのかということです。

更新: さらに明確にするために - big_lookup_object は共有ルックアップ オブジェクトです。それを分割して個別に処理する必要はありません。私はそれの単一のコピーを保持する必要があります。それを分割するために必要な作業は、他の多くの大きなファイルを読み取り、ルックアップ オブジェクトに対してそれらの大きなファイル内のアイテムを検索することです。

さらに更新: データベースは優れたソリューションです。この質問では、メモリ内ソリューションに特に興味がありました。最終的な解決策として Hadoop を使用しますが、ローカルのメモリ内バージョンも使用できるかどうかを確認したかったのです。

4

8 に答える 8

59

マルチプロセッシングによって生成された子プロセスは、プログラムで以前に作成されたオブジェクトを共有しますか?

Python < 3.8 ではいいえ、Python ≥ 3.8 でははい

プロセスには独立したメモリ空間があります。

解決策 1

多くのワーカーを含む大規模な構造を最大限に活用するには、次のようにします。

  1. 各ワーカーを「フィルター」として記述します – から中間結果を読み取りstdin、動作し、中間結果を に書き込みますstdout

  2. すべてのワーカーをパイプラインとして接続します。

    process1 <source | process2 | process3 | ... | processn >result
    

各プロセスは読み取り、作業、および書き込みを行います。

すべてのプロセスが同時に実行されるため、これは非常に効率的です。書き込みと読み取りは、プロセス間の共有バッファーを直接通過します。


解決策 2

場合によっては、より複雑な構造 (多くの場合、ファンアウト構造) があります。この場合、複数の子を持つ親がいます。

  1. 親がソース データを開きます。親は多くの子をフォークします。

  2. 親はソースを読み取り、同時に実行されている各子にソースの一部を提供します。

  3. 親が最後に到達したら、パイプを閉じます。子はファイルの終わりを取得し、正常に終了します。

子の部分は、それぞれの子が単に読むだけなので、書くのが楽しいですsys.stdin

親は、すべての子をスポーンし、パイプを適切に保持するために少し派手なフットワークを持っていますが、それほど悪くはありません。

ファンインは逆の構造です。独立して実行されている多くのプロセスは、それらの入力を共通のプロセスにインターリーブする必要があります。コレクターは、多くのソースから読み取る必要があるため、書くのは簡単ではありません。

多くの名前付きパイプからの読み取りは、selectどのパイプに保留中の入力があるかを確認するモジュールを使用して行われることがよくあります。


解決策 3

共有ルックアップは、データベースの定義です。

解決策 3A – データベースをロードします。ワーカーがデータベース内のデータを処理できるようにします。

解決策 3B – werkzeug (または同様のもの) を使用して非常に単純なサーバーを作成し、HTTP GET に応答する WSGI アプリケーションを提供して、ワーカーがサーバーにクエリを実行できるようにします。


解決策 4

共有ファイルシステム オブジェクト。Unix OS は共有メモリ オブジェクトを提供します。これらはメモリにマップされた単なるファイルであるため、従来のバッファリングされた読み取りの代わりに I/O のスワップが行われます。

いくつかの方法で Python コンテキストからこれを行うことができます

  1. (1) 元の巨大なオブジェクトを小さなオブジェクトに分割し、(2) それぞれ小さなオブジェクトでワーカーを起動するスタートアップ プログラムを作成します。小さいオブジェクトは、ファイルの読み取り時間を少し節約するために、Python オブジェクトをピクルすることができます。

  2. (1) 元の巨大なオブジェクトを読み取り、操作を使用してページ構造のバイトコード ファイルを書き込み、seek個々のセクションが単純なシークで簡単に見つけられるようにするスタートアップ プログラムを作成します。これがデータベース エンジンの機能です。データをページに分割し、seek.

この大きなページ構造のファイルにアクセスできるワーカーを生成します。各作業者は、関連する部分を探して、そこで作業を行うことができます。

于 2009-03-18T20:06:08.807 に答える
39

プログラムの前半で作成されたマルチプロセッシング共有オブジェクトを介して生成された子プロセスはありますか?

場合によります。グローバル読み取り専用変数の場合、(消費されたメモリを除いて)そう見なされることがよくありますが、そうでない場合はそうではありません。

マルチプロセッシングのドキュメントには次のように書かれています。

Better to inherit than pickle/unpickle

Windowsでは、子プロセスがそれらを使用できるように、マルチプロセッシングの多くのタイプを選択可能にする必要があります。ただし、通常、パイプまたはキューを使用して共有オブジェクトを他のプロセスに送信することは避けてください。代わりに、他の場所で作成された共有リソースへのアクセスを必要とするプロセスが祖先プロセスからプログラムを継承できるように、プログラムを調整する必要があります。

Explicitly pass resources to child processes

Unixでは、子プロセスは、グローバルリソースを使用して親プロセスで作成された共有リソースを利用できます。ただし、子プロセスのコンストラクターに引数としてオブジェクトを渡すことをお勧めします。

コードを(潜在的に)Windowsと互換性を持たせることとは別に、これにより、子プロセスがまだ生きている限り、オブジェクトが親プロセスでガベージコレクションされないようにすることもできます。これは、オブジェクトが親プロセスでガベージコレクションされたときに一部のリソースが解放された場合に重要になる可能性があります。

Global variables

子プロセスで実行されたコードがグローバル変数にアクセスしようとすると、表示される値(存在する場合)は、Process.start()が呼び出されたときの親プロセスの値と同じではない可能性があることに注意してください。 。

Windows(シングルCPU)の場合:

#!/usr/bin/env python
import os, sys, time
from multiprocessing import Pool

x = 23000 # replace `23` due to small integers share representation
z = []    # integers are immutable, let's try mutable object

def printx(y):
    global x
    if y == 3:
       x = -x
    z.append(y)
    print os.getpid(), x, id(x), z, id(z) 
    print y
    if len(sys.argv) == 2 and sys.argv[1] == "sleep":
       time.sleep(.1) # should make more apparant the effect

if __name__ == '__main__':
    pool = Pool(processes=4)
    pool.map(printx, (1,2,3,4))

sleep

$ python26 test_share.py sleep
2504 23000 11639492 [1] 10774408
1
2564 23000 11639492 [2] 10774408
2
2504 -23000 11639384 [1, 3] 10774408
3
4084 23000 11639492 [4] 10774408
4

なしsleep

$ python26 test_share.py
1148 23000 11639492 [1] 10774408
1
1148 23000 11639492 [1, 2] 10774408
2
1148 -23000 11639324 [1, 2, 3] 10774408
3
1148 -23000 11639324 [1, 2, 3, 4] 10774408
4
于 2009-03-18T23:17:12.623 に答える
34

S.ロットは正しいです。Python のマルチプロセッシング ショートカットは、効果的に、複製された個別のメモリ チャンクを提供します。

ほとんどの *nix システムでos.fork()は、実際には、will への下位レベルの呼び出しを使用すると、コピー オン ライト メモリが得られます。私の知る限り、理論的には、可能な限り最も単純なプログラムでは、データを複製せずにそのデータから読み取ることができます。

ただし、Python インタープリターでは、物事はそれほど単純ではありません。オブジェクト データとメタデータは同じメモリ セグメントに格納されるため、オブジェクトがまったく変更されない場合でも、そのオブジェクトの参照カウンタがインクリメントされるとメモリ書き込みが発生し、コピーが発生します。「print 'hello'」以上の処理を行うほとんどすべての Python プログラムでは、参照カウントが増加するため、コピー オン ライトの利点を実感することはおそらくないでしょう。

誰かが Python で共有メモリ ソリューションをハッキングできたとしても、プロセス全体でガベージ コレクションを調整しようとすると、おそらくかなりの苦痛を伴うことになるでしょう。

于 2009-03-18T20:45:37.997 に答える
8

Unix で実行している場合、fork の仕組みにより、同じオブジェクトを共有する場合があります(つまり、子プロセスには別のメモリがありますが、コピーオンライトであるため、誰も変更しない限り共有される可能性があります)。私は次のことを試しました:

import multiprocessing

x = 23

def printx(y):
    print x, id(x)
    print y

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)
    pool.map(printx, (1,2,3,4))

そして、次の出力を得ました:

$ ./mtest.py
23 22995656
1
23 22995656
2
23 22995656
3
23 22995656
4

もちろん、これはコピーが作成されていないことを証明するpsものではありませんが、 の出力を見て、各サブプロセスが実際にどれだけのメモリを使用しているかを確認することで、状況を確認できるはずです。

于 2009-03-18T20:44:52.350 に答える
3

プロセスが異なれば、アドレス空間も異なります。インタープリターのさまざまなインスタンスを実行するようなものです。それが IPC (プロセス間通信) です。

この目的には、キューまたはパイプのいずれかを使用できます。後でネットワーク経由でプロセスを配布する場合は、rpc over tcp を使用することもできます。

http://docs.python.org/dev/library/multiprocessing.html#exchange-objects-between-processes

于 2009-03-18T20:14:54.823 に答える
1

multiprocessing 自体には直接関係ありませんが、あなたの例からは、shelveモジュールなどを使用できるようです。「big_lookup_object」は本当に完全にメモリ内にある必要がありますか?

于 2009-03-18T21:35:08.517 に答える