3

私はPythonでいくつかの極端な奇妙な振る舞いを観察しています。次のコードを検討してください。

from multiprocessing import Process  
import scipy

def test():
    pass

for i in range(1000):
    p1 = Process(target=test)
    p1.start()
    p1.join()
    print i

これに対してstrace-fを実行すると、ループから次のセグメントが取得されます。

clone(Process 19706 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b23afde1970) = 19706
[pid 19706] set_robust_list(0x2b23afde1980, 0x18) = 0
[pid 18673] wait4(19706, Process 18673 suspended
 <unfinished ...>
[pid 19706] stat("/apps/python/2.7.1/lib/python2.7/multiprocessing/random", 0x7fff041fc150) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.so", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/randommodule.so", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.py", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/apps/python/2.7.1/lib/python2.7/multiprocessing/random.pyc", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 19706] open("/dev/urandom", O_RDONLY) = 3
[pid 19706] read(3, "\3\204g\362\260\324:]\337F0n\n\377\317\343", 16) = 16
[pid 19706] close(3)                    = 0
[pid 19706] open("/dev/null", O_RDONLY) = 3
[pid 19706] fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
[pid 19706] exit_group(0)               = ?
Process 18673 resumed
Process 19706 detached

ファイルシステム内で「ランダム」を検索することについてのすべてのがらくたはどうですか?クラスター上でこの構造を使用して非常に多くのプロセスを並行して実行し、非常に高速にループしているため、これを避けたいと思います。この種のファイルシステムアクティビティは、ファイルシステムメタデータサーバーを詰まらせているため、クラスター管理者は私に教えてくれます。 。

「importscipy」コマンドを削除すると、この問題は解決します。

clone(Process 23081 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b42ec15e970) = 23081
[pid 23081] set_robust_list(0x2b42ec15e980, 0x18) = 0
[pid 22052] wait4(23081, Process 22052 suspended
 <unfinished ...>
[pid 23081] open("/dev/null", O_RDONLY) = 3
[pid 23081] fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
[pid 23081] exit_group(0)               = ?
Process 22052 resumed
Process 23081 detached

しかし、実際のコードにはscipyが必要なので、それを取り除くことはできません。または多分私はできますが、それは苦痛でしょう。

なぜ私がこの振る舞いを見ているのか誰かが知っていますか?それが私が実行している何かのいくつかのバージョンの癖である場合:

python:2.7.1、マルチプロセッシング:0.70a1、scipy:0.9.0、

実際、システムに依存している可能性があることに気付いたので、ラップトップで同じコードを実行し、問題はありませんでした(つまり、2番目のケースと同等の出力)。私が実行しているラップトップで

python:2.6.5、マルチプロセッシング:0.70a1、scipy:0.10.0、

おそらく、修正されたのは以前のバージョンのscipyの問題またはバグですか?このようなものを検索しても何も見つかりませんでした。それが問題であるとしても、クラスター上のscipyのバージョンを変更するのはそれほど簡単ではありませんが、必要に応じてクラスター管理者に新しいバージョンをビルドさせることはおそらく可能です。

これが問題になる可能性がありますか?

4

4 に答える 4

5

これは、Windowsまたは__main__モジュールが原因ではありません。また、これはPythonがビジネスを行うのが好きな方法でもありません。また、再確認すると、変更された2.7を実行していない限り、これはPython 2.6の動作であり、2.7ではないことがわかると思います。

この問題は、モジュール内のランダムモジュール初期化ステップに起因することは完全に正しいです。これは、プロセスがn個のワーカーmultiprocessing.forkingを生成するときに、すべてがまったく同じ一連の疑似を進めるワーカーを作成することを防ぐように設計されています。乱数(たとえば、それらがすべてそれらの番号を使用してSSL接続をネゴシエートしている場合、セキュリティを危険にさらす可能性があります):

        if 'random' in sys.modules:
            import random
            random.seed()

ただし、ここで重要なのは、システムコールの観点からは、上記のimportステートメントsys.modulesノーオペレーションである必要があることを理解することです。モジュール名がディクショナリにキーとしてすでに存在importする場合は、そこで見つかった値を返すだけだからです。ファイルシステムから何かをロードしようとせずに:

>>> import sys
>>> sys.modules['fake'] = 'Not even a module'
>>> import fake
>>> fake
'Not even a module'

したがってif、上記で引用したステートメントは、モジュールがロードされていない場合に余分な費用がかからないようにすることを特に目的としています。ロードせずに実験を行うと、ステートメントの本文が起動することはありません。importrandomscipyif

では、何が問題なのでしょうか。

問題は、2.7より前の古いバージョンのPythonでは、パッケージ内にあるモジュールで、の相対的なインポートを試みているか、最上位のパッケージのインポートを試みている可能性があるという2つの異なる意味を持っていることです。 。このあいまいで高価な動作が最近のバージョンのPythonで変更された理由の詳細については、PEP328を参照してください。import foothe_package.foofoo

http://legacy.python.org/dev/peps/pep-0328/

このような背景があるので、strace出力を確認して、ここでの回答にまだ誰も言及していないことに気付くことができます。リストされstat()open()いるシステムコールは、モジュールをインポートしようとしているのではなく、 !という名前の存在しないモジュールをインポートしようとしています。randommultiprocessing.random

これは、すでにリストされているにもかかわらず、追加のインポートが試行されている重要な理由です。Python2.6が、ステートメントが実際にインポートを目的としているという仮定にフォールバックできるようになる前に、代わりにインポートされる可能性を排除する必要があるためです。ステートメントがサブモジュールのコードに表示されるため、の相対インポートを試行します。randomsys.modulesimportrandommultiprocessing.randomimportmultiprocessing.forking

sys.modules['random'].seed()プログラマーは、これらの余分なシステムコールを回避するために、新たにインポートを試みるのではなく、実際に言ったはずです。ただし、Pythonの最新バージョンにアップグレードする機会があれば、この動作に長く悩まされることはないでしょう。

于 2014-07-24T16:52:05.593 に答える
1

これは、モジュールをインポートするときにpythonが行うことです。何も悪いことはありません。最初のアクセス後、とにかくファイルシステムのキャッシュにあるので、これが問題を引き起こしている可能性はほとんどありません。

Pythonは、PYTHONPATH内のすべてのフォルダーで、指定された名前のモジュールが持つ可能性のあるすべての有効な名前をチェックします。ダイナミックライブラリを使用するコンパイル済みプログラムを実行する場合にも同様のことが起こります。ダイナミックリンカは、ライブラリが見つかるまで、ライブラリのさまざまな場所も検索します。

于 2012-06-11T02:07:17.603 に答える
1

さて、ThiefMasterは完全に正しく、何も問題はないようですが、私はまだそれが好きではなく、回避するつもりです。しかし、最初に、これが起こっていることです。multiprocessing.forkingでは、次のことが発生します。

class Popen(object):

    def __init__(self, process_obj):
        sys.stdout.flush()
        sys.stderr.flush()
        self.returncode = None

        self.pid = os.fork()
        if self.pid == 0:
            if 'random' in sys.modules:
                import random
                random.seed()
            code = process_obj._bootstrap()
            sys.stdout.flush()
            sys.stderr.flush()
            os._exit(code)

したがって、「random」がsys.modulesにある場合、それは実際にrandomをインポートし、それを使用して新しいランダムシードを生成します。一部のアプリケーションでこれを自動的に実行するのは良いことだと思いますが、私は確かにそれを予期していなかったでしょう。おそらくそれを行うのには十分な理由がありますが、私はそれを行う必要はありません。

私のマルチプロセッシングのニーズは非常に単純なので、今は自分でフォークを実行しています。

    childpid = os.fork()
    if childpid == 0:
        ...run code...
        os._exit(0)
    else:
        os.waitpid(childpid, 0)

もちろん、これはインポートを行わないので、何も検索しません。マルチプロセッシングの適切なビットをサブクラス化し、「インポート」を削除するだけで、検索を終了することもできます。同じバージョンのマルチプロセッシングを実行していたので、ラップトップで検索が行われなかった理由はわかりません。

于 2012-06-12T03:35:23.233 に答える
0

あなたのオペレーティングシステムは何ですか?Windowsだと思います。ThiefMasterが指摘したように、動作は正常ですが、ループの反復ごとにそれを取得する理由は、おそらくマルチプロセッシングがWindowsにモジュールをインポートするためです。__main__if __name__=="__main__"ブロック内のループを保護してみてください。

于 2012-06-11T05:25:38.673 に答える