2017-01-04 11 views
1

私はメモリフットプリントを減らそうとしています。私が使用できるメモリの最大量はわずか500MBです。 .split('\t')とforループを使用すると、実際には多くのメモリが使用されているようです。このメモリ使用量を減らす方法はありますか?forループとリストからメモリフットプリントを減らす

Line # Mem usage Increment Line Contents 
============================================== 
10  35.4 MiB  0.0 MiB @profile 
11        def function(username): 
12  35.4 MiB  0.0 MiB  key = s3_bucket.get_key(username) 
13  85.7 MiB  50.2 MiB  file_data = key.get_contents_as_string() 
14 159.3 MiB  73.6 MiB  g = [x for x in file_data.splitlines() if not x.startswith('#')] 
15 144.8 MiB -14.5 MiB  del file_data 
16 451.8 MiB 307.1 MiB  data = [x.split('\t') for x in g] 
17 384.0 MiB -67.8 MiB  del g 
18 
19 384.0 MiB  0.0 MiB  d = [] 
20 661.7 MiB 277.7 MiB  for row in data: 
21 661.7 MiB  0.0 MiB   d.append({'key': row[0], 'value':row[3]}) 
22 583.7 MiB -78.0 MiB  del data 
25 700.8 MiB 117.1 MiB  database[username].insert_many(d) 
26 700.8 MiB  0.0 MiB  return 

UPDATE1

@ジャンFrançoisFabreと@Torxedの提案を1として、それは改善だが、発電機は、まだ大量のメモリを取るように見えます。私は、キーを反復処理し、.insert()を実行するようにMongoDB .insert_many()を使用することを好むだろう

@martineauが遅くくらいです。

20  35.3 MiB  0.0 MiB @profile 
21        def function(username): 
22  85.4 MiB  50.1 MiB  file_data = s3_bucket.get_key(username).get_contents_as_string() 
23 610.5 MiB 525.2 MiB  data = (x.split('\t') for x in isplitlines(file_data) if not x.startswith('#')) 
24 610.5 MiB  0.0 MiB  d = ({'key': row[0], 'value':row[3]} for row in data) 
25 123.3 MiB -487.2 MiB  database[username].insert_many(d) 
26 123.3 MiB  0.0 MiB  return 

UDPATE2

このプロファイルが示すように、私はメモリ使用量のソースを特定しました:

21  41.6 MiB  0.0 MiB @profile 
22        def insert_genotypes_into_mongodb(username): 
23  91.1 MiB  49.4 MiB  file_data = s3_bucket.get_key(username).get_contents_as_string() 
24  91.1 MiB  0.0 MiB  genotypes = (x for x in isplitlines(file_data) if not x.startswith('#')) 
25  91.1 MiB  0.0 MiB  d = ({'rsID': row.split('\t')[0], 'genotype':row.split('\t')[3]} for row in genotypes) 
26         # snps_database[username].insert_many(d) 
27  91.1 MiB  0.0 MiB  return 

insert_many()機能を明確にロードするリスト全体を引き起こして、前の行を解決しますメモリとプロファイラを混乱させる。すべての助けを

22  41.5 MiB  0.0 MiB @profile 
23        def insert_genotypes_into_mongodb(username): 
24  91.7 MiB  50.2 MiB  file_data = s3_bucket.get_key(username).get_contents_as_string() 
25 180.2 MiB  88.6 MiB  genotypes = (x for x in isplitlines(file_data) if not x.startswith('#')) 
26 180.2 MiB  0.0 MiB  d = ({'rsID': row.split('\t')[0], 'genotype':row.split('\t')[3]} for row in genotypes) 
27  91.7 MiB -88.6 MiB  chunk_step = 100000 
28 
29  91.7 MiB  0.0 MiB  has_keys = True 
30 127.4 MiB  35.7 MiB  keys = list(itertools.islice(d,chunk_step)) 
31 152.5 MiB  25.1 MiB  while has_keys: 
32 153.3 MiB  0.9 MiB   snps_database[username].insert_many(keys) 
33 152.5 MiB  -0.9 MiB   keys = list(itertools.islice(d,chunk_step)) 
34 152.5 MiB  0.0 MiB   if len(keys) == 0: 
35 104.9 MiB -47.6 MiB    has_keys = False 
36         # snps_database[username].insert_many(d[i*chunk_step:(i+1)*chunk_step]) 
37 104.9 MiB  0.0 MiB  return 

ありがとう:

ソリューションは、チャンクでのMongoDBにキーを挿入しています。

+0

ジェネレータとジェネレータ式を反復(または他の怠惰に評価された構造体)をリストの代わりに使用します。 –

+0

私はこれについては分かりませんが、 'del g'と' del data'の後に 'import gc'と' gc.collect() '文でガベージコレクションを強制しようとしました。 –

+1

'data = [x in gのx.split( '\ t')]これはあなたがイテレータとしてリストを使用していないためです。使用しているのは基本的には' x = list(something) 'です。 'data'変数を作成する前にすべてのデータが収集されるのを待ちます。代わりに 'for obj in x.split()'を使用してください。 – Torxed

答えて

1

最初に、listを作成するときはsplitlines()を使用しないでください。イテレータが必要です。だから、splitlines()のイテレータバージョンを取得するIterate over the lines of a string例を使用することができます。

def isplitlines(foo): 
    retval = '' 
    for char in foo: 
     retval += char if not char == '\n' else '' 
     if char == '\n': 
      yield retval 
      retval = '' 
    if retval: 
     yield retval 

個人的なメモ:これは、文字列の連結を非常に効率的ではありません。私はリストとstr.joinを使って書き直しました。私のバージョンは:

def isplitlines(buffer): 
    retval = [] 
    for char in buffer: 
     if not char == '\n': 
      retval.append(char) 
     else: 
      yield "".join(retval) 
      retval = [] 
    if retval: 
     yield "".join(retval) 

、その後、使用されている(行が1つの分割除く)なし中間リストとしてdelを使用しないでください。ただ、g部分を飛ばして、あなたのコードを「圧縮」し、代わりにリスト内包の発電理解としてdを作成します。

def function(username): 
    key = s3_bucket.get_key(username) 
    file_data = key.get_contents_as_string() 
    data = (x.split('\t') for x in isplitlines(file_data) if not x.startswith('#')) 
    d = ({'key': row[0], 'value':row[3]} for row in data) 
    database[username].insert_many(d) 

これはもう少し「onelined」することができるが、理解するのは難しいだろう。現在のコードはOKです。 file_data

+0

ありがとう!私の更新された記事を参照してください改善がまだ非常に大きなフットプリントを持っています。 – WillJones

+0

BTWは誰かがその行 '24 610.5 MiB 0.0 MiB d =({'key':行[0]、 '値':行[3]}の行の説明をすることができます。値は何ですか?ビフォアーアフター?ジェネレータの宣言(実行されていない)がその多くのメモリを割り当てているのはなぜですか?これはすべての鍵となるかもしれません。 –

+0

はい - これはメモリプロファイラの戻り値です:https://pypi.python.org/pypi/memory_profiler – WillJones

0

ソリューションは、チャンクでのMongoDBにキーを挿入している:メモリの唯一の一つの大きな源の塊と一緒に作業チェーンジェネレータの理解/表現としてのことを参照してください

22  41.5 MiB  0.0 MiB @profile 
23        def insert_genotypes_into_mongodb(username): 
24  91.7 MiB  50.2 MiB  file_data = s3_bucket.get_key(username).get_contents_as_string() 
25 180.2 MiB  88.6 MiB  genotypes = (x for x in isplitlines(file_data) if not x.startswith('#')) 
26 180.2 MiB  0.0 MiB  d = ({'rsID': row.split('\t')[0], 'genotype':row.split('\t')[3]} for row in genotypes) 
27  91.7 MiB -88.6 MiB  chunk_step = 100000 
28 
29  91.7 MiB  0.0 MiB  has_keys = True 
30 127.4 MiB  35.7 MiB  keys = list(itertools.islice(d,chunk_step)) 
31 152.5 MiB  25.1 MiB  while has_keys: 
32 153.3 MiB  0.9 MiB   snps_database[username].insert_many(keys) 
33 152.5 MiB  -0.9 MiB   keys = list(itertools.islice(d,chunk_step)) 
34 152.5 MiB  0.0 MiB   if len(keys) == 0: 
35 104.9 MiB -47.6 MiB    has_keys = False 
36         # snps_database[username].insert_many(d[i*chunk_step:(i+1)*chunk_step]) 
37 104.9 MiB  0.0 MiB  return 
関連する問題