52

いくつかのファイルをメモリにロードしようとしています。ファイルには、次の 3 つの形式のいずれかがあります。

  • 文字列 TAB int
  • 文字列タブフロート
  • int TAB フロート。

実際、これが解決に役立つ場合に備えて、それらは ngram 静的ファイルです。例えば:

i_love TAB 10
love_you TAB 12

現在、私が今やっている疑似コードは

loadData(file):
     data = {}
     for line in file:
        first, second = line.split('\t')
        data[first] = int(second) #or float(second)

     return data

驚いたことに、ディスク内のファイルの合計サイズは約 21 MB ですが、メモリにロードすると、プロセスは 120 ~ 180 MB のメモリを必要とします。(Python アプリケーション全体が他のデータをメモリにロードすることはありません)。

10 個未満のファイルがありますが、現在数百万行ある 1 つのファイルを除いて、それらのほとんどは約 50 ~ 80k 行で安定しています。

そこで、メモリ消費量を削減するためのテクニック/データ構造をお願いしたいと思います:

  • 圧縮技術に関するアドバイスはありますか?
  • まだ dict を使用している場合、メモリを減らす方法はありますか? Python dict の Java のように「負荷係数」を設定することは可能ですか?
  • 他のデータ構造がある場合は、速度をいくらか犠牲にしてメモリを削減することもできます。それにもかかわらず、これは時間に敏感なアプリケーションであるため、ユーザーがクエリを入力すると、結果を返すのに数秒以上かかることはあまり合理的ではないと思います. これに関しては、Google が Google 翻訳を非常に高速に実行する方法に今でも驚いています。彼らは多くの技術と多くのサーバーの能力を使用しているに違いありません。

どうもありがとうございました。アドバイスをお待ちしております。

4

6 に答える 6

84

メモリフットプリントを改善するのに役立つ完全な戦略を提供することはできませんが、何がこれほど多くのメモリを使用しているのかを正確に分析するのに役立つと思います。

Pythonの辞書の実装(ハッシュテーブルの比較的単純な実装)、および組み込みの文字列と整数のデータ型の実装を見ると、たとえばここ(具体的にはobject.h、intobject)です。 .h、stringobject.h、dictobject.h、および../Objects内の対応する* .cファイル)では、予想されるスペース要件をある程度正確に計算できます。

  1. 整数は固定サイズのオブジェクトです。つまり、参照カウント、型ポインター、および実際の整数が含まれます。通常、32ビットシステムでは少なくとも12バイト、64ビットシステムでは24バイトですが、余分なスペースは考慮されていない可能性があります。アライメントによって失われました。

  2. 文字オブジェクトは可変サイズです。つまり、

  • 参照カウント

  • タイプポインタ

  • サイズ情報

  • 怠惰に計算されたハッシュコードのためのスペース

  • 状態情報(例:インターン文字列に使用)

  • 動的コンテンツへのポインタ

    合計で、32ビットの場合は少なくとも24バイト、64ビットの場合は60バイト。文字列自体のスペースは含まれません。

  1. 辞書自体はいくつかのバケットで構成されており、各バケットには次のものが含まれています。
  • 現在保存されているオブジェクトのハッシュコード(使用されている衝突解決戦略のため、バケットの位置からは予測できません)

  • キーオブジェクトへのポインタ

  • 値オブジェクトへのポインタ

    合計で、32ビットで少なくとも12バイト、64ビットで24バイト。

  1. 辞書は8つの空のバケットから始まり、容量に達するたびにエントリ数を2倍にすることでサイズが変更されます。

32ビットマシンで、セットアップと同様に、それぞれが整数に関連付けられた46,461個の一意の文字列(337,670バイトの連結文字列サイズ)のリストを使用してテストを実行しました。上記の計算によると、最小のメモリフットプリントは

  • 46,461 *(24 + 12)バイト=文字列/整数の組み合わせの場合は1.6 MB
  • 337,670=文字列の内容は0.3MB
  • 65,536 *12バイト=ハッシュバケットの場合は1.6MB(13回のサイズ変更後)

合計2.65MB。(64ビットシステムの場合、対応する計算では5.5 MBになります。)

Pythonインタープリターをアイドル状態で実行している場合、-toolによるフットプリントはps4.6MBです。したがって、ディクショナリの作成後に予想される合計メモリ消費量は、約4.6 + 2.65 = 7.25MBです。私のテストでの実際のメモリフットプリント(によるps)は7.6MBでした。私は余分な約を推測します。0.35 MBは、Pythonのメモリ割り当て戦略(メモリアリーナなど)によって生成されたオーバーヘッドによって消費されました。

もちろん、多くの人がps、メモリフットプリントを測定するための私の使用は不正確であり、32ビットおよび64ビットシステムでのポインタタイプと整数のサイズに関する私の仮定は、多くの特定のシステムでは間違っている可能性があることを指摘します。承諾する。

しかし、それにもかかわらず、重要な結論は次のとおりです。

  • Python辞書の実装は、驚くほど少量のメモリを消費ます
  • しかし、参照カウント、事前に計算されたハッシュコードなどのために、多くのintおよび(特に)文字列オブジェクトが占めるスペースは、最初に考えたよりも多くなります。
  • Pythonを使用し、文字列と整数を個々のオブジェクトとして表現したい限り、メモリのオーバーヘッドを回避する方法はほとんどありません。少なくとも、それがどのように行われるかはわかりません。
  • キーと値を(Pythonオブジェクトではなく)Cポインターとして格納するハッシュを実装するPython-C拡張機能を探す(または自分で実装する)ことは価値があるかもしれません。それが存在するかどうかはわかりません。しかし、それが可能であり、メモリフットプリントを半分以上削減できると私は信じています。
于 2012-04-22T05:11:57.347 に答える
6

ディスクには文字列だけがあり、Python にロードするとき、インタープリターは文字列自体の他に、各文字列と各辞書の構造全体を作成する必要があります。

dict が使用するメモリを減らす方法はありませんが、問題に対処する方法は他にもあります。速度と引き換えにメモリを使用する場合は、すべてをメモリ内の辞書にロードするのではなく、SQLite ファイルから文字列を格納してクエリを実行することを検討する必要があります。

于 2012-04-22T03:31:11.280 に答える
4

Trie ( http://en.m.wikipedia.org/wiki/Trie ) のデータ構造のように思えますが、メモリ効率に対する要求に適しているかもしれません。

更新: python dict のメモリ効率が問題として提起されましたが、サードパーティのライブラリが利用可能であるため、標準ライブラリから拒否されました。参照: http://bugs.python.org/issue9520

于 2012-04-22T05:53:00.913 に答える
3

数値データを Python のメモリにコンパクトに格納しようとしている場合、最適なソリューションはおそらく Numpy です。

Numpy ( http://numpy.org ) は、ネイティブ C 構造を使用してデータ構造を割り当てます。そのデータ構造のほとんどは、単一のデータ型を保存していると想定しているため、これはすべての状況に対応しているわけではありません (null を保存する必要がある場合など)。あなたは求めることができます。多くの科学がそれで行われます (参照: SciPy)。

もちろん、次のオプションがある場合は zlibです。

  • 十分な CPU サイクル、および
  • メモリに収まらない大量のデータ

データの「ページ」を配列として宣言し(どんなに大きくても)、データを読み込んで配列に保存し、圧縮してから、さらにデータを読み込んで、すべてのデータをメモリに入れることができます。欲しいです。

次に、ページを繰り返し、解凍し、配列に変換して、必要に応じて操作を行います。例えば:

def arrayToBlob(self, inArray):
    a = array.array('f', inArray)
    return a.tostring()

def blobToArray(self, blob, suppressWarning=False):
    try:
        out = array.array('f', [])
        out.fromstring(blob)
    except Exception, e:
        if not suppressWarning:
            msg = "Exception: blob2array, err: %s, in: %s" % (e, blob)
            self.log.warning(msg)
            raise Exception, msg
    return out

データを BLOB として取得したら、この BLOB をzlibに渡してデータを圧縮できます。繰り返される値が多数ある場合、このブロブは大幅に圧縮される可能性があります。

もちろん、すべてを非圧縮のままにしておくよりも遅くなりますが、メモリに収まらない場合は、最初から選択肢が限られています。

圧縮しても、すべてがメモリに収まらない場合があり、その時点で、圧縮されたページを文字列やピクルなどとして書き出す必要がある場合があります。

幸運を!

于 2014-12-01T17:10:12.610 に答える
3

dict をblist.sorteddictに置き換えて、メモリのオーバーヘッドなしで log(n) アクセスできます。ディクショナリとまったく同じように動作するため便利です。つまり、すべてのメソッドを実装しているため、初期型を変更するだけで済みます。

于 2013-10-29T18:47:17.533 に答える