いくつかのファイルをメモリにロードしようとしています。ファイルには、次の3つの形式のいずれかがあります。
確かに、これが解決に役立つ場合のために、それらは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個未満のファイルがあり、それらのほとんどは、現在何百万行あるファイルを除いて、約50〜80k行で安定しています。
そこで、メモリ消費を削減するためのテクニック/データ構造を求めたいと思います。
どうもありがとうございました。あなたのアドバイスを楽しみにしています。
メモリフットプリントを改善するのに役立つ完全な戦略を提供することはできませんが、メモリを大量に消費しているものを正確に分析するのに役立つと考えています。
辞書のPython implementation(ハッシュテーブルの比較的単純な実装)、および組み込みの文字列と整数データ型の実装を見ると、たとえば here (具体的には、object.h、intobject.h、stringobject.h、dictobject.h、および../Objectsの対応する* .cファイル)、予想されるスペースをある程度の精度で計算できます。要件:
整数は固定サイズのオブジェクトです。つまり、参照カウント、タイプポインター、および実際の整数が含まれます。通常、32ビットシステムでは少なくとも12バイト、24バイト 64ビットシステムでは、アライメントによって失われる可能性のある余分なスペースは考慮されません。
stringオブジェクトは可変サイズです。つまり、
合計で少なくとも24バイト32ビットまたは60バイト64ビットで、文字列自体のスペースを含まない。
辞書自体はいくつかのバケットで構成され、各バケットには
合計で少なくとも12バイト32ビットで24バイト64ビットで。
辞書は8個の空のバケツで始まり、その容量に達するたびに2倍にリサイズされるエントリ数になります。
私はテストを実施しました32ビットマシンで、46,461の一意の文字列(連結文字列サイズ337,670バイト)のリストを使用して、それぞれ整数に関連付けられています-セットアップと同様です。上記の計算によれば、最小メモリフットプリントは
合計2.65 MB。 (64ビットシステムの場合、対応する計算は5.5 MBになります。)
Pythonインタープリターをアイドル状態で実行すると、ps
- toolによるフットプリントは4.6 MBです。したがって、ディクショナリを作成した後に予想される合計メモリ消費量は約4.6 + 2.65 =7.25 MBです。私のテストでの真のメモリフットプリント(ps
による)は7.6 MBです。私は余分なCAを推測します。 Pythonのメモリ割り当て戦略(メモリアリーナなど)によって生成されたオーバーヘッドによって0.35 MBが消費されました
もちろん、多くの人々は、メモリフットプリントを測定するためのps
の使用が不正確であり、32ビットおよび64ビットシステムでのポインタタイプと整数のサイズに関する私の仮定が多くの特定で間違っている可能性があることを指摘するでしょうシステム。認めた。
しかし、それにもかかわらず、主要な結論、これらは次のように思われます:
1)メモリ内のSQLiteは素晴らしいソリューションのように聞こえます。ロードされたデータをより簡単にクエリできるようになります。
sqlite3.connect( ':memory:')
2)おそらく名前付きのタプルが必要です-辞書よりも軽く、ドット表記を使用してプロパティにアクセスできると確信しています(とにかく美的好みがあります)。
http://docs.python.org/dev/library/collections
3)Redisをご覧ください: https://github.com/andymccurdy/redis-py
これは高速であり、物事を簡単に持続させることができます。つまり、使用するたびにセット全体をロードする必要はありません。
4)トライは良いアイデアのように聞こえますが、作業の終わりにいくつかの理論的な複雑さを追加します。ただし、Redisを使用して実装および保存することもできます。これにより、速度がさらに向上します。
しかし、全体として、名前付きタプルはおそらくここでのトリックです。
ディスクには文字列だけがあり、Pythonにロードするとき、インタープリターは文字列自体に加えて、各文字列および各辞書の構造全体を作成する必要があります。
Dictによって使用されるメモリを削減する方法はありませんが、問題にアプローチする他の方法があります。メモリの速度と引き換えに喜んでいる場合は、メモリ内の辞書にすべてをロードするのではなく、SQLiteファイルの文字列を保存およびクエリすることを検討する必要があります。
トライ( http://en.m.wikipedia.org/wiki/Trie )のようなデータ構造は、メモリ効率の向上に適しています。
更新:python dictのメモリ効率が問題として提起されましたが、サードパーティライブラリの可用性を考慮して標準ライブラリから拒否されました。参照: http:// bugs.python.org/issue952
メモリオーバーヘッドなしでlog(n)アクセスのdictを blist.sorteddict に置き換えることができます。辞書とまったく同じように動作する、つまりすべてのメソッドを実装するため、初期型を変更するだけで済むので便利です。
数値データをコンパクトにpythonメモリに保存しようとする場合、おそらく最良の解決策はNumpyです。
Numpy( http://numpy.org )は、ネイティブC構造を使用してデータ構造を割り当てます。そのデータ構造のほとんどは、単一のデータ型を格納していることを前提としているため、これはすべての状況(nullなどを格納する必要があるかもしれません)ではありませんが、非常に、非常に、非常に高速で、ほぼ同じくらいコンパクトですあなたが求めることができます。多くの科学がそれで終わりました(SciPyも参照)。
もちろん、別のオプションがあります:zlib、以下がある場合:
データの「ページ」(ただし、必要な大きさ)を配列として宣言し、データを読み取り、配列に格納し、Zipしてから、メモリにすべてのデータがあるまで、さらにデータを読み込むことができます。欲しいです。
次に、ページを繰り返し処理し、解凍し、配列に変換して、必要に応じて操作を行います。例えば:
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
データをブロブとして取得したら、このブロブをzlibに渡し、データを圧縮できます。繰り返し値が多数ある場合、このblobは大幅に圧縮できます。
もちろん、すべてを非圧縮のままにするよりも時間がかかりますが、メモリに収まらない場合は、最初から選択が制限されます。
圧縮しても、すべてがメモリに収まらない場合があります。その時点で、圧縮されたページを文字列やピクルスなどとして書き出す必要があります。
がんばろう!