47

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

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

ラッパー クラスを書いた時点では、mmap モジュールについて知りませんでした。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]

ただし、ファイルサイズに制限はありません。さほど難しいことはありません。

4

8 に答える 8

38

IEEE 1003.1 から:

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

すべての仮想アドレス空間が必要なのは、まさにそれmmap() .

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

于 2009-11-02T16:52:46.003 に答える
18

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

PythonのドキュメントにはPOSIXmmapについては記載されていないため、(私が行ったように)POSIXの知識があまりないPythonプログラマーとしてやってきた場合、アドレス空間の問題は非常に恣意的で、設計が不適切に見えます。

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

于 2009-11-03T11:04:31.337 に答える
17

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

編集:あなたが見逃している明らかなことは、どのオペレーティングシステムでも、mmap の仕組みを理解することです。これにより、ファイルの一部をメモリの範囲にマップできます。一度マップすると、ファイルのその部分へのアクセスは可能な限り最小限のオーバーヘッドで発生します。マッピングが 1 回行われ、別の範囲にアクセスするたびに変更する必要がないため、オーバーヘッドが低くなります。欠点は、マップしようとしている部分に十分なオープン アドレス範囲が必要なことです。ファイル全体を一度にマッピングする場合は、メモリ マップに、ファイル全体を収めるのに十分な大きさの穴が必要になります。そのような穴が存在しない場合、またはアドレス空間全体よりも大きい場合、失敗します。

于 2009-11-02T16:01:05.093 に答える
9

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

于 2009-11-02T20:48:42.750 に答える
6

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

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

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

于 2009-11-02T17:34:48.507 に答える
4

64 ビット OS と 64 ビット Python 実装を備えた 64 ビット コンピューターを使用するか、mmap()

mmap() 数 GiB を超える大きなファイルを処理するには、CPU ハードウェア サポートが必要です。

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

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

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

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

于 2016-12-13T04:35:15.613 に答える
2

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

于 2009-11-02T15:42:06.903 に答える
1

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

于 2009-11-02T17:40:28.903 に答える