2013-05-15 12 views
7

私は、64ビットのPython 3.3.0はCPythonインタプリタを使用して、64ビットのLinux(カーネル バージョン2.6.28.4)マシン上で実行されている(生物学)カスタムシミュレータを持っています。パイプを使用してプロセス間でPythonオブジェクトを転送する際のバイト制限はありますか?

シミュレータは、私は実験を実行するために並列処理に建て 、有効な結果を得るために多くの独立した実験に依存するため。 スレッド間の通信は、主に管理 multiprocessing Queue S(doc)と生産者 - 消費者パターンの下で発生します。次のように アーキテクチャのランダウンは:

  • 産卵及び管理Process ESおよびシミュレーションを消費
  • 1の結果、消費者の処理を行う様々なQueue S
  • Nワーカープロセスを処理するマスター・プロセスシミュレーション結果と結果のソートと分析

マスタプロセスとワーカープロセスは、入力Queueで通信します。 同様に、ワーカープロセスは結果を出力Queueに配置します。これは、結果コンシューマプロセスがアイテムを消費する です。最終ResultConsumer オブジェクトは、バックマスタープロセスにmultiprocessing Pipedoc) を介して渡されます。

すべてが、それはPipeを経由して戻って にマスタープロセスをResultConsumerオブジェクトを渡すしようとするまで正常に動作します:私は最初の2つのトレース(Process、ライブラリ内の未処理終了)、 を理解

Traceback (most recent call last): 
    File "/home/cmccorma/.local/lib/python3.3/multiprocessing/process.py", line 258, in _bootstrap 
    self.run() 
    File "/home/cmccorma/.local/lib/python3.3/multiprocessing/process.py", line 95, in run 
    self._target(*self._args, **self._kwargs) 
    File "DomainArchitectureGenerator.py", line 93, in ResultsConsumerHandler 
    pipeConn.send(resCon) 
    File "/home/cmccorma/.local/lib/python3.3/multiprocessing/connection.py", line 207, in send 
    self._send_bytes(buf.getbuffer()) 
    File "/home/cmccorma/.local/lib/python3.3/multiprocessing/connection.py", line 394, in _send_bytes 
    self._send(struct.pack("!i", n)) 
struct.error: 'i' format requires -2147483648 <= number <= 2147483647 

と3番目はResultConsumerオブジェクトを Pipeまでマスタプロセスに送信するためのコードです。最後の2つのトレースは、 が興味深いところです。 Pipeピクルスそれに送信され、他端に 得られたバイトがrecv()実行時 アンピクルある(マッチング接続)を通過する任意のオブジェクト。 self._send_bytes(buf.getbuffer())は、 にピクルされたオブジェクトのバイトを送信しようとしています。 self._send(struct.pack("!i", n))は で、長さnの整数(ネットワーク/ビッグエンディアン)で構造体をパックしようとします。 ここで、nはパラメータとして渡されるバッファの長さです(struct ライブラリはPython値とC構造体 Python文字列として、the docを参照してください)。

このエラーは、多くの実験を試みる場合にのみ発生します。 10回の実験 はそれを引き起こすことはありませんが、1000は常に有効です(他のすべてのパラメータは定数です)。これまでstruct.errorがスローされる理由の私の最高 仮説がパイプを押し下げることしようとしたバイト の数は2^32-1(2147483647)、または〜2ギガバイトを超えていることです。

だから私の質問は二つある:

  1. 私は_structからstruct.py基本的にちょうど 輸入としての私の調査で立ち往生していると私はそれがある見当がつかない。

  2. 基本的なアーキテクチャがすべて 64ビットであると仮定すると、バイト制限は任意であるように見えます。だから、なぜ私はそれ以上のものを渡すことができないのですか?さらに、私が がこれを変更できない場合は、この問題の良い(読んだ:簡単な)回避策がありますか?

注:私はQueue年代は、同様の酸洗中間ステップを使用している疑いがあるとしてPipeの代わりにQueueを使用すると、問題、 を解決するとは思いません。 編集:このメモは、abarnertの回答で指摘されているとおり、完全に間違っています。

+0

あなたが本当にばかばかしい回避策を望むなら、あなたは 'process.py'を開いてその行を長いものに変更することができますが、どこにでも行かなければならないでしょう。 。 –

+0

@SuperDisk: 'process.py'に関連する行はありません。長いものに変更するintはありません(Python 3.xではlong型でなく、2.7でも同じ型です) 。また、 'multiprocessing'をmonkeypatchするか、それをforkして別のコピーを保持し、stdlibを適切に修正する方がはるかに優れています。 – abarnert

+0

@abarnertトレースバックから349行目のように見えます。 'i'を 'q'に変更します(これはstructモジュールにとっては長いです)。おそらく私は間違っていますか? –

答えて

8

私はstruct.pyとして基本的には_structからのインポートだけで、私はそれがどこにあるのか分かりません。 CPythonので

_structはソースツリー内Modulesディレクトリに_struct.cから構築されたC拡張モジュールです。あなたはコードhereをオンラインで見つけることができます。

foo.pyimport _fooの場合は、ほとんど常にC拡張モジュールです(通常は_foo.cから作成されます)。 foo.pyがまったく見つからない場合は、おそらく_foomodule.cから構築されたC拡張モジュールです。

PyPyを使用していない場合でも、しばしばequivalent PyPy sourceを調べる価値があります。これらは、純粋なPythonのほぼすべての拡張モジュールを再実装します。残りの部分(このケースを含む)では、C言語ではなく、RPythonです。

ただし、この場合、何も知る必要はありませんどのようにstructがドキュメントの内容を超えて動作しているかについて


バイト制限は、基礎となるアーキテクチャは、すべて64ビットであることを考える任意思われます。それは呼び出し元のコードで

ルック:あなたがthe documentationを見れば

self._send(struct.pack("!i", n)) 

'i'フォーマット文字は、明示的に「ssize_tが何であれ」ではない、意味「4バイトのC整数」。そのためには、'n'を使用する必要があります。または、長いlongを明示的に使用する場合は、'q'とすることもできます。

multiprocessingにはstruct.pack('!q', n)を使用することができます。または'!q'。または、長さをstruct以外の何らかの方法でエンコードします。これはもちろん、非パッチ付きのmultiprocessingとの互換性を失うことになります。これは、複数のコンピュータや何かに分散処理を行わせようとすると問題になる可能性があります。しかし、それは非常に簡単でなければなりません:

def _send_bytes(self, buf): 
    # For wire compatibility with 3.2 and lower 
    n = len(buf) 
    self._send(struct.pack("!q", n)) # was !i 
    # The condition is necessary to avoid "broken pipe" errors 
    # when sending a 0-length buffer if the other end closed the pipe. 
    if n > 0: 
     self._send(buf) 

def _recv_bytes(self, maxsize=None): 
    buf = self._recv(8) # was 4 
    size, = struct.unpack("!q", buf.getvalue()) # was !i 
    if maxsize is not None and size > maxsize: 
     return None 
    return self._recv(size) 

もちろん、この変更が十分であるという保証はありません。あなたは、周囲のコードの残りの部分を読んで、それから地獄をテストしたいと思うでしょう。


注:私はQueue年代は、同様の酸洗中間ステップを使用している疑いがあるとしてPipeの代わりにQueueを使用して、問題を解決することはありませんと思われます。

まあ、この問題は酸洗いとは関係ありません。 Pipeは長さを送信するためにpickleを使用していません。structを使用しています。 pickleにこの問題がないことを確認できます。pickle.loads(pickle.dumps(1<<100)) == 1<<100Trueを返します。

(以前のバージョンでは、またpickleは巨大なオブジェクト - 例えば、あなたが現在ヒットしているものと同じ高8倍速程度のスケールで問題を引き起こしている可能性の要素-2Gののlistに問題がありました。しかし、それはです3.3で修正されました。)

一方...それが動作するかどうかを調べるためにソースを掘り下げるのではなく、試してみるのはもっと速いでしょうか?


また、暗黙的な酸洗によって実際に2GBのデータ構造を渡したいと思いますか?

私は遅く、メモリが大変なことをしていたなら、それを明示的にすることが望ましいでしょう。たとえば、一時ファイルにピクルスし、パスまたはfdを送ります。 (numpyまたはpandasなどを使用している場合は、pickleの代わりにバイナリファイル形式を使用しますが、同じ考えです)。

さらに、データを共有することをおすすめします。はい、変更可能な共有状態が悪いです...しかし不変のオブジェクトを共有しています。あなたが2GBのものを持っていても、multiprocessing.sharedctypesで​​の配列や構造体(配列や構造体など)に入れたり、のうちmmapから​​両側に、または...?構造を定義して選ぶ余分なコードが少しありますが、そのメリットが大きくなる可能性があるときは、試してみる価値があります。あなたはPythonでバグ/明らかに欠けている機能/不合理な制限を見つけたと思うとき


最後に、それはバグトラッカーを見て価値があります。 issue 17560: problem using multiprocessing with really big objects?はまさにあなたの問題であり、示唆された回避策を含む多くの情報があります。

+0

うわー、信じられないほど包括的な答えをありがとう!あなたのポイントのいくつかに取り組む:2GBのデータ構造を回すという問題は、これまで問題ではありませんでした。ちょうど最近、より多くのデータを吐き出す新しいシミュレーションモデルが登場しました。シリアル化とデータの受け渡しについてより明確にするために内部構造を再構築する必要があるか、またはメインプロセスから必要なロジックを結果のプロセスと問題を完全に取り除く。私はしなかったバグトラッカーを見てみることさえあるので、それにも感謝します! –

+0

@CollinM:それでは、回避策を追加して、後で内部構造を変更すること(それはデータを吐き出したり、別の方法で共有するかどうかなど)を検討し始めたようです。それが役に立てば幸い。 – abarnert

関連する問題