クイックスタートガイド:Python 3.5では、メモリが約5GBと予想されるものの、代わりに15GBを超えてリソースが不足してクラッシュする。辞書のリストに名前付きタプルを追加すると膨大なメモリが肥大化する
import pickle
from collections import namedtuple
Hdr1 = namedtuple("Hdr1", "id hash source options elements locations")
Hdr2 = namedtuple("Hdr2", "id hash source options stream locations")
Hdr3 = namedtuple("Hdr3", "id hash source options series locations")
Identifier = namedtuple("Identifier", "id hash")
Location = namedtuple("Location", "index tell")
IndexData = namedtuple("IndexData", "filenames packet1 packet2 packet3")
filenames = [] # filled by other code, but it's a list, with 10 items
packet1_d = {}
packet2_d = {}
packet3_d = {}
index_data = IndexData(filenames, packet1_d, packet2_d, packet3_d)
# for each file
# read all the packets in the file, get the tell() location each time
if packet is header:
if packet is packet1_header:
packet1_d[Identifier(id, hash)] = Hdr1(id, hash, source, options, [])
elif packet is packet2_header:
packet2_d[Identifier(id, hash)] = Hdr2(id, hash, source, options, stream, [])
else
packet3_d[Identifier(id, hash)] = Hdr3(id, hash, source, options, series, [])
else
loc = Location(index, tell)
# This part below is deadly
if packet is packet1:
packet1_d[Identifier(id, hash)].locations.append(loc)
if packet is packet2:
packet2_d[Identifier(id, hash)].locations.append(loc)
if packet is packet3:
packet3_d[Identifier(id, hash)].locations.append(loc)
pickle.dump(index_data, open("index_data.p", "wb"))
詳細:これは明らかに、すべてのコードではありません - 私が開いて、ファイルを解析部品を保持し、あなたが問題を再現することはできませんので、明らかにあなたは、使用可能なファイルを持っていません。 。 is
ステートメントは擬似コードですが、論理的には同等です。これはデータ構造をどのように設定するのかを正しく表しているため、メモリ使用量の見積もりが正確で、変数の使用方法を正確に表しているため、メモリリークを検出するための代表的な方法です。
「致命的」とコメントした6行をコメントアウトすると、10 GBのデータと約100 Mのパケットを実行した後、ピケットファイル(ファイル名のみと異なるパケットヘッダーのリスト)がどこかにあります5〜10 MBです。私はpickle圧縮を知っていますが、それでも "ベースデータ"は50MB未満です。
合計91,116,480データパケットがあります。計算を簡単にするために、100Mと呼ぶだけです。各Location
は、ファイルのリストへの索引と、file.tell()
からの戻り値です。
>>> import sys
>>> from collections import namedtuple
>>> Location = namedtuple("Location", "idx tell")
>>> fobj = open("/really/big/data.file", "rb")
>>> fobj.seek(1000000000)
1000000000
>>> tell = fobj.tell()
>>> loc = Location(9, tell)
>>> sys.getsizeof(loc)
64
だから、総メモリ使用量が6.4ギガバイト以下でなければならない:対話型シェルでの実証試験では、それぞれ「場所」は64バイトであると言います。
これはなぜ15 GB以上のメモリを占有するのですか?このデータを設定するためのメモリ効率的な方法がありますか?
私はこれをすべてのデータをsqliteデータベースファイルに入れていました。ファイル全体は2.1GBなので、生データは2.1GBを超えてはいけないようです。私は6GBの範囲でそれを得るPythonのオーバーヘッドを理解することができますが、それは15 GBを打つべきではありません。私はこの問題を回避することはできましたが、次回は回避する方法を知りたいと思います。
'sys.getsizeof(' _tuple_オブジェクトのみのサイズであり、含まれているオブジェクトのサイズではありません)Pythonの各整数は約28バイト(64ビットビルド)の独立オブジェクトです。そしてピクルリング中に、プロセスはネイティブオブジェクトのインスタンスとシリアル化されたバージョンの両方をメモリに保持する必要があります(Pikcleは結果をファイルに書き込んでいるので、Pikcleはそれをしてはいけませんが、すでにエンコードされたオブジェクト循環参照を避けるため) – jsbueno
タプル+ 2つのintが120バイトだと言っているのでしょうか?その100Mデータポイントは、そこの問題の多くを占めるでしょう。その場合、答えはタプルを使用しないことです。 Mathiasのような型付き配列の提案は、メモリフットプリントを大幅に減らすでしょうか? –
私はまだsqliteを使っています。さもなければ、あなたの値を8バイトに保持する名前付きタプルではないカスタムクラスはいいでしょうが、あなたはまだオブジェクトのオーバーヘッドを持っています。 1、2、3の3要素配列にはかなりのオーバヘッドもあります.Nubmersを超えてオブジェクト情報を保持する必要があるからです。 – jsbueno