メモリ効率の高い文字列の文字列マップのPython(C)
-
26-09-2019 - |
質問
必要メモリ効率の高いデータ構造のための保存を約百万鍵と値のペアは、キーの文字列のバイトで約80、値が文字列の約200バイトの総キーと値のサイズについては280MB.私も効率的なルックアップの値は、キーによって好ましくは、ハッシュ。メモリーの頭上に必要な、例えばのための280MBの有用なデータは、データ構造なり300MBの仮想メモリを含む malloc()
架かるようにします。利用パターンは次の通りです:まずは空のデータ構造としての移植では徐々に、変化する鍵とな変化の長さの値です。どのプラス、データ構造をサポートの長さを変更する値の100%の値をオーバーヘッドの意味するx値をバイトxのバイトが無駄に一時的に未利用バッファになります。
いpure Pythonモジュール内蔵のPythonモジュール、またはCを実施好ましくは(C)がPythonランタイム環境を含んでいます。かばすことができたserialize全体のデータ構造ディスクの読みいます。
けることを証明するような小さなオーバーヘッドで私がシンプルなデザインと 開への対応, のハッシュテーブルの125万要素を含む4バイトのポインタ1MBデータブロック、データブロックを含むキーと値の長さとして ベース-128varints.この設計に重要な制限:することがないの除去または変化のペアをなくそのメモリです。自分の計算1万鍵と値のペアの280バイトは、それぞれオーバーヘッドの未満3.6%(10 080 000バイト単位)。この制限上記よりごすことができること、20 000 000バイトのオーバーヘッド。
私た http://www.pytables.org/ は、高速アクセスメモリ効率の高い包装のデータです。いつもっと注意深くチェックだった。
解決 10
私はしっかりとメモリをパックする任意の既存のソリューションを見つけることができなかったので、私は自分自身のためにCでそれを実装することに決めました。問題の<私>のアドレッシング開いと私のデザインを参照してください。
他のヒント
[OK]を、汚れ単純なアプローチ。
データ構造のためのPythonの辞書を使用してください。私は、キーは80の文字と値200文字だった100万のランダムなキーと値のペアでPythonの辞書を満たしました。それはせいぜい300メガバイトのあなた仕様の外にある、自分のコンピュータ上で360844 KBを取ったが、それはまだかなりのメモリが効率的ですので、私はとにかく解決策として、それを提供します。
これはまた、CのAPIを持っていることのあなたの条件に失敗しました。あなたがCを必要とする理由私はわからないんだけど、問題は、Pythonをタグ付けし、Cタグを欠いているとして、私はそれだけで法案に合うかもしれないかどうかを確認するために、純粋なPythonのを提供します。
持続性について。 cPickleモジュールを使用してください。それは非常に高速で、再び、汚れ簡単です。あなたの辞書を保存するには:
cPickle.dump(mydict, "myfile.pkl")
あなたの辞書をリロードします:
mydict = cPickle.load("myfile.pkl")
第汚れシンプルなアイデアは、基本的には、ディスクベースのPythonの辞書であるshelve
モジュールを使用することです。メモリのオーバーヘッドは(それがディスク上のすべてです)非常に低いです。しかし、それははるかに遅いもいます。
マルタインコメント(答えないでくださいなぜ人のコメント)でこれを述べたが、私は同意する:利用SQLiteのを。あなたはそれを試してみると、それはあなたのニーズを満たすかどうかを確認する必要があります。
は、これは難しいことではありません。削除は、断片化につながるます。
また、固定長キーにコミットする必要があります。あなたは80のバイトを述べました。あなたの鍵は複製することが許可されていますか?そうでない場合、それはさらに簡単です。
だから、ここであなたは何をすべきかです。
あなたはの配列を作成します
struct {
char value[80];
char *data;
} key;
そして、あなたは、この配列がソートキープます。
あなたのキーを複製することができます場合は、あなたが必要があります。
struct link {
char *data;
link *next;
}
struct {
char value[80];
link *data;
} key;
(マイCはさびているが、これはその要旨である)後者は、値のリンクされたリストに各キーポインティングを有している。
その後、ルックアップは単純なバイナリ検索です。 「疼痛」とは、この配列を維持し、挿入/キーを削除しています。それが聞こえるように、それは痛みを伴うようではないが、それは、特に64ビットシステムでは、大量のメモリを節約します。
何を減らしたいことはポインタの数です。あなたはポインタで満たされた構造の多くを持っているとき、ポインタは高価です。 64ビットシステムでは、ポインタは8バイトです。だから、単一のポインタのために、あなたのメモリ予算8MBのは、そこに行く。
だから、あなたはすぐに)万行を持つことになりますし、それにコミットすることができ、その後のmalloc(1000000 *はsizeof(キー)、「知っている」場合の費用は、(メモリをコピーして圧縮、配列を構築している、それは)拡張中にあなたにいくつかのコピーを保存するでしょう。
しかし、それは最高だし、ランニング、パフォーマンスはかなり良いです後、恐れることはありません。現代のCPUは、実際にはメモリ周りの100Mブロックをコピーするにはかなり良いです。
だけはさておきとして、私はJavaでこのような多くの何かをしました。 25Mエントリを持つ64ビットJVM、地図上のRAMの2Gです。 (これに類似した技術を使用して)私のソリューションは)600M付近で持っています。 JavaはCよりも多くのポインタを使用しますが、前提は同じです。
あなたは簡単な辞書を使用してみましたか?オーバーヘッドが要件に収まる可能性があるので、あなたのデータのほとんどは、文字列である。
あなたは、キーの代わりに、キー自体のsha1
を使用することができます。キーが一意である場合は、キーのsha1
ハッシュは、あまりにも、そうです。それはあなたの限界の下できしむ音にしようとするメモリの節約を提供しています。
from random import choice
from string import letters
from hashlib import sha1
def keygen(length):
return "".join(choice(letters) for _ in xrange(length))
def gentestdata(n=1000*1000):
# return dict((sha1(keygen(80)).digest(), keygen(200)) for _ in xrange(n))
d = {}
for _ in xrange(n):
key = sha1(keygen(80)).digest()
assert key not in d
value = keygen(200)
d[key] = value
return d
if __name__ == '__main__':
d = gentestdata()
私のUbuntuのボックスで、これはメモリの304メガバイトで実施トップスます:
2010-10-26 14:26:02 hbrown@hbrown-ubuntu-wks:~$ ps aux | grep python
[...]
hbrown 12082 78.2 7.5 307420 303128 pts/1 S+ 14:20 4:47 python
を閉じるに十分な?これのpythonのではなく、Cます。
<時間>以降:あなたのデータはやや冗長である場合も、あなたは値をgzip
することができます。それは宇宙のトレードオフの時間です。
SQLiteを使用では良い方法がありました。迅速に実施できるんだ 十分速く、余裕があります。 となります
まるでロール自分のいの
どきを予測する対数、または上限ですか?
どきを予測する計データサイズ、または上限ですか?
アリーナアロケータ 文字列およびノード。(通常んの作業のリストは、アリーナをかけて予測の合計サイズ)。
配置によって異なりアルゴリズム、原則として使うパックでバイト-タイトのみでオーバーヘッドおoverallocation、低に影響する作業セットです。
ただし、行為cmp/コピーなど。事業これらの文字列を記憶すると以下の保証までを絞ったからこれらの文字列操作
- すべての要素は、CPUの言葉を揃えて
- すべてのパッドのバイト(例えば)0
- できる安全な読み"を超えて"文字列で終了していないクロのCPUボーダー
ハッシュテーブル の指数です。辞書いものの、その場合のみ意味をな可能性の劣化やそれを単に混ぜれば深刻な問題です。かず"株式"には、ハッシュテーブル実装のためのCがあろ?でしょ?で置き換え配分への呼び出しのアロケータ.
メモリの産地
ができることを保証するルックアップのない依頼していない文字列の地図るべき店舗の鍵を別のアリーナ、必要なだけのハッシュの衝突.まで向上できるメモリの産地となる。(その場合、まして終了後の完了実績報告"を定義しなければいけませんもののコピー衝突の鍵を新規アリーナ、捨てるのです。の利点はいく限界がとても多いのだ。
分離で傷つきによっては、アクセスパターン場合は、通常、使用価値を一度に各ルックアップを持って対的に同じアリーナがあります。場合など見上げる数のキーを使用し、その値を繰り返し、別途、分野で自分たちで作るものだ。"
ご支援に"面白い文字で"/Unicodeで、正常の文字列の前に格納します。
を使用できるstructモジュールをパックのバイナリデータと解凍できます。実践できるメモリ効率的な保管を使用します。私がアクセスできます。
はApacheポータブルランタイムは、Cベースのハッシュテーブルを有しています。あなたは http://apr.apache.org/docs/apr/でドキュメントを参照してくださいすることができます0.9 / GROUP_ の4月の_hash.html の
すべてのお店apr_hash_tでvoid *型です。だから、それはあなたの価値を完全に制御できます。あなたがしたい場合はSOあなたの代わりに、文字列の実際の長さの100バイトのブロックへのポインタを格納することができます。
Judyすべきメモリ効率の高い: http://judy.sourceforge.net/
(ベンチマーク: http://www.nothings.org/computer/judy/, は、"データ構造のサイズ").
参照: http://www.dalkescientific.com/Python/PyJudy.html
また、
のためのキーの固定サイズがあり http://panthema.net/2007/stx-btree/ C++でいるので、ただ、カスタムCの包装を使用できるからCPython).場合、データセットが可能ですので、可変長のキーの値および使用のハッシュまたは接頭辞は、可変長の鍵として固定長の鍵となる。
同じ論理に適用され http://google-opensource.blogspot.ru/2013/01/c-containers-that-save-memory-and-time.html や http://code.google.com/p/sparsehash/ -isteadの重std::stringとしてキーを使用32ビットまたは64ビット整数型キーでなんとから実際の可変長の鍵となる。