web-dev-qa-db-ja.com

Pythonのmmapが大きなファイルで機能しないのはなぜですか?

[編集:この問題は32ビットシステムにのみ適用されます。コンピュータ、OS、およびpython実装が64ビットの場合、巨大ファイルのmmap-ingは確実に機能し、非常に効率的です。]

特に、ファイルへのビット単位の読み取りアクセスを許可するモジュールを作成しています。ファイルは大きくなる可能性があるため(数百GB)、ファイルを文字列のように扱い、すべてのシークと読み取りを非表示にする単純なクラスを作成しました。

当時、ラッパークラスを作成しましたが、 mmap module については知りませんでした。 mmapのドキュメントを読んだとき、私は思った "素晴らしい-これは私が必要とするものだけです。コードを取り出してmmapで置き換えます。おそらくもっと効率的で、常に削除するのが良いでしょう。コード。"

問題は、mmapが大きなファイルに対して機能しないことです。これはおそらく最も明白なアプリケーションだと思ったので、これは私にとって非常に驚くべきことです。ファイルが数ギガバイトを超える場合、EnvironmentError: [Errno 12] Cannot allocate memory。これは、32ビットのPythonビルドでのみ発生するため、アドレス空間が不足しているようですが、これに関するドキュメントは見つかりません。

私のコードは

f = open('somelargefile', 'rb')
map = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

だから私の質問はここに何か明らかなものが欠けていますか? mmapを大きなファイルで移植可能に動作させる方法はありますか、それとも単純なファイルラッパーに戻る必要がありますか?


更新:Python mmapにはPOSIX mmapと同じ制限があるはずです。ここで私の欲求不満をよりよく表現するために、 mmap。

import os

class Mmap(object):
    def __init__(self, f):
        """Initialise with a file object."""
        self.source = f

    def __getitem__(self, key):
        try:
            # A slice
            self.source.seek(key.start, os.SEEK_SET)
            return self.source.read(key.stop - key.start)
        except AttributeError:
            # single element
            self.source.seek(key, os.SEEK_SET)
            return self.source.read(1)

これは読み取り専用であり、特別なことはしませんが、mmapと同じようにこれを行うことができます。

map2 = Mmap(f)
print map2[0:10]
print map2[10000000000:10000000010]

ただし、ファイルサイズに制限はありません。本当に難しいことではありません...

44
Scott Griffiths

IEEE 1003.1から:

Mmap()関数は、プロセスのアドレス空間とファイル、共有メモリオブジェクト、または[TYM]型付きメモリオブジェクトの間のマッピングを確立します。

それがmmap()doesであるため、すべての仮想アドレス空間が必要です。

それが本当にメモリ不足であるという事実は問題ではありません-利用可能な以上のアドレス空間をマップすることはできません。その後、結果を取得してあたかもあったメモリであるかのようにアクセスするので、2 ^ 32バイトを超えるファイルにアクセスすることをどのように正確に提案しますか? mmap()が失敗しなかったとしても、32ビットアドレス空間のスペースが不足する前に、最初の4GBしか読み取ることができませんでした。もちろん、ファイル上でmmap()スライディング32ビットウィンドウを使用できますが、必要な回数を制限するようにアクセスパターンを最適化できない限り、必ずしもメリットは得られません。以前のウィンドウにアクセスします。

37
Nick Bastin

私自身の質問に答えて申し訳ありませんが、私が持っていた本当の問題は、mmapが特定の特性と制限を備えた標準のPOSIXシステムコールであり、Python mmapが単に公開することになっていることであることを認識していませんでしたその機能。

PythonのドキュメントではPOSIX mmapについては触れられていないため、PythonプログラマとしてPOSIXについての知識がほとんどない場合)アドレススペースの問題はかなり恣意的で、設計が不適切です。

Mmapの本当の意味を教えてくれた他のポスターに感謝します。残念ながら、大きなファイルを文字列として扱うために私の手作りのクラスのより良い代替案を提案した人は誰もいなかったので、今のところそれに固執する必要があります。多分私はそれをクリーンアップし、機会があればモジュールのパブリックインターフェイスの一部にします。

17
Scott Griffiths

32ビットのプログラムとオペレーティングシステムは、最大32ビットのメモリ、つまり4GBのみをアドレス指定できます。合計をさらに小さくする他の要因があります。たとえば、Windowsはハードウェアアクセス用に0.5〜2 GBを予約します。もちろん、プログラムもある程度のスペースを取ります。

編集:不足している明らかなことは、任意のオペレーティングシステムでのmmapのメカニズムの理解です。これにより、ファイルの一部をメモリの範囲にマップできます。いったんそれを実行すると、ファイルのその部分へのアクセスは、最小限のオーバーヘッドで行われます。マッピングは1回だけ行われるためオーバーヘッドが低く、別の範囲にアクセスするたびに変更する必要はありません。欠点は、マップしようとしている部分に十分なオープンアドレス範囲が必要なことです。ファイル全体を一度にマッピングする場合は、メモリマップに、ファイル全体を収めるのに十分な大きさの穴が必要です。そのようなホールが存在しない場合、またはアドレススペース全体よりも大きい場合、失敗します。

16
Mark Ransom

mmapモジュールは、大きなファイルを操作するために必要なすべてのツールを提供しますが、他の人々が述べた制限のため、一度にマッピングすることはできません一度にすべて。一度に適切なサイズのチャンクをマッピングし、いくつかの処理を行ってから、そのマッピングを解除して別のチャンクをマッピングできます。 mmapクラスへの主要な引数はlengthoffsetであり、これらはまさにそのように機能し、lengthバイトをバイトからマッピングできるようにしますマップされたファイルのoffset。マップされたウィンドウの外にあるメモリのセクションを読みたいときはいつでも、新しいウィンドウにマップする必要があります。

あなたが見逃している点は、mmapは、要求されたデータ範囲全体で任意のアクセスのためにファイルをメモリにマップするメモリマッピング関数であるということです。

あなたが探しているものは、いつでも大きなデータ構造の小さなウィンドウを見ることができるAPIを提示するある種のデータウィンドウクラスのように聞こえます。このウィンドウの境界を超えてアクセスすることは、データウィンドウの独自のAPIを呼び出す以外には不可能です。

これは問題ありませんが、メモリマップではありません。より制限のあるAPIを犠牲にして、より広いデータ範囲の利点を提供するものです。

6
morechilli

64ビットOSと64ビットのpython実装、またはmmap()を使用しないで、64ビットコンピューターを使用します

mmap()requires数GiBより大きい大きなファイルで意味のあるCPUハードウェアサポート。

CPUの [〜#〜] mmu [〜#〜] と割り込みサブシステムを使用して、すでにRAMにロードされているかのようにデータを公開できます。

MMUはハードウェアであり、物理的でないデータに対応するアドレスにアクセスするたびに割り込みを生成しますRAM=これは実行時に意味があるため、アクセスするコードは、データがRAMに収まらないことを決して知りません(または知る必要はありません)。

これにより、アクセスコードを簡単に記述できます。ただし、mmap()をこのように使用するには、関連するすべてのものが64ビットアドレスを処理する必要があります。

または、mmap()を完全に回避して、独自のメモリ管理を行うことが望ましい場合があります。

3
RGD2

Lengthパラメーターをゼロに設定しています。これは、ファイル全体をマップすることを意味します。 32ビットのビルドでは、ファイルの長さが2GB(おそらく4GB)を超える場合、これは不可能です。

2
Rod Hyde

OSにファイル全体をメモリ範囲にマップするように要求します。読み取り/書き込みによってページフォールトをトリガーするまで読み取られませんが、範囲全体がプロセスで使用可能であることを確認する必要があります。その範囲が大きすぎると、問題が発生します。

1
Macke