13

Ubuntu9.04で実行されているPython2.6.2[GCC4.3.3]を使用しています。Pythonスクリプトを使用して、大きなデータファイル(〜1GB、> 300万行)を1行ずつ読み取る必要があります。

以下の方法を試してみましたが、非常に大きなメモリスペース(〜3GB)を使用していることがわかりました

for line in open('datafile','r').readlines():
   process(line)

また、

for line in file(datafile):
   process(line)

大きなファイルを1行ずつロードするより良い方法はありますか?

  • a)ファイルがメモリに一度にロードできる最大行数を明示的に言及することによって?または
  • b)サイズのチャンク、たとえば1024バイトでロードすることにより、チャンクの最後の行が切り捨てられることなく完全にロードされる場合はどうなりますか?

いくつかの提案は私が上で述べた方法を与え、すでに試しました、私はこれを処理するためのより良い方法があるかどうかを見ようとしています。私の検索は今のところ実り多いものではありません。私はあなたの助けに感謝します。

p / sを使用してメモリプロファイリングを実行しましたが、使用HeapyしているPythonコードでメモリリークは見つかりませんでした。

2012年8月20日16:41(GMT + 1)更新

JF Sebastian、mgilson、およびIamChuckBによって提案された両方のアプローチを試しました(データファイルは変数です)

with open(datafile) as f:
    for line in f:
        process(line)

また、

import fileinput
for line in fileinput.input([datafile]):
    process(line)

不思議なことに、どちらも最大3GBのメモリを使用します。このテストでの私のデータファイルのサイズは、21,181,079行で構成される765.2MBです。3GBで安定する前に、メモリが時間とともに増加する(約40〜80MBステップ)のがわかります。

基本的な疑問、使用後にラインをフラッシュする必要がありますか?

これをよりよく理解するために、Heapyを使用してメモリプロファイリングを行いました。

レベル1プロファイリング

Partition of a set of 36043 objects. Total size = 5307704 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  15934  44  1301016  25   1301016  25 str
     1     50   0   628400  12   1929416  36 dict of __main__.NodeStatistics
     2   7584  21   620936  12   2550352  48 tuple
     3    781   2   590776  11   3141128  59 dict (no owner)
     4     90   0   278640   5   3419768  64 dict of module
     5   2132   6   255840   5   3675608  69 types.CodeType
     6   2059   6   247080   5   3922688  74 function
     7   1716   5   245408   5   4168096  79 list
     8    244   1   218512   4   4386608  83 type
     9    224   1   213632   4   4600240  87 dict of type
<104 more rows. Type e.g. '_.more' to view.>

================================================== ==========

レベル1-インデックス0のレベル2プロファイリング

Partition of a set of 15934 objects. Total size = 1301016 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0   2132  13   274232  21    274232  21 '.co_code'
     1   2132  13   189832  15    464064  36 '.co_filename'
     2   2024  13   114120   9    578184  44 '.co_lnotab'
     3    247   2   110672   9    688856  53 "['__doc__']"
     4    347   2    92456   7    781312  60 '.func_doc', '[0]'
     5    448   3    27152   2    808464  62 '[1]'
     6    260   2    15040   1    823504  63 '[2]'
     7    201   1    11696   1    835200  64 '[3]'
     8    188   1    11080   1    846280  65 '[0]'
     9    157   1     8904   1    855184  66 '[4]'
<4717 more rows. Type e.g. '_.more' to view.>

レベル1のレベル2プロファイリング-インデックス1

Partition of a set of 50 objects. Total size = 628400 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0     50 100   628400 100    628400 100 '.__dict__'

レベル1-インデックス2のレベル2プロファイリング

Partition of a set of 7584 objects. Total size = 620936 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0   1995  26   188160  30    188160  30 '.co_names'
     1   2096  28   171072  28    359232  58 '.co_varnames'
     2   2078  27   157608  25    516840  83 '.co_consts'
     3    261   3    21616   3    538456  87 '.__mro__'
     4    331   4    21488   3    559944  90 '.__bases__'
     5    296   4    20216   3    580160  93 '.func_defaults'
     6     55   1     3952   1    584112  94 '.co_freevars'
     7     47   1     3456   1    587568  95 '.co_cellvars'
     8     35   0     2560   0    590128  95 '[0]'
     9     27   0     1952   0    592080  95 '.keys()[0]'
<189 more rows. Type e.g. '_.more' to view.>

レベル1-インデックス3のレベル2プロファイリング

Partition of a set of 781 objects. Total size = 590776 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0      1   0    98584  17     98584  17 "['locale_alias']"
     1     29   4    35768   6    134352  23 '[180]'
     2     28   4    34720   6    169072  29 '[90]'
     3     30   4    34512   6    203584  34 '[270]'
     4     27   3    33672   6    237256  40 '[0]'
     5     25   3    26968   5    264224  45 "['data']"
     6      1   0    24856   4    289080  49 "['windows_locale']"
     7     64   8    20224   3    309304  52 "['inters']"
     8     64   8    17920   3    327224  55 "['galog']"
     9     64   8    17920   3    345144  58 "['salog']"
<84 more rows. Type e.g. '_.more' to view.>

================================================== ==========

レベル2-インデックス0、レベル1-インデックス0のレベル3プロファイリング

Partition of a set of 2132 objects. Total size = 274232 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0   2132 100   274232 100    274232 100 '.co_code'

レベル2-インデックス0、レベル1-インデックス1のレベル3プロファイリング

Partition of a set of 50 objects. Total size = 628400 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0     50 100   628400 100    628400 100 '.__dict__'

レベル2-インデックス0、レベル1-インデックス2のレベル3プロファイリング

Partition of a set of 1995 objects. Total size = 188160 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0   1995 100   188160 100    188160 100 '.co_names'

レベル2-インデックス0、レベル1-インデックス3のレベル3プロファイリング

Partition of a set of 1 object. Total size = 98584 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0      1 100    98584 100     98584 100 "['locale_alias']"

まだこれをトラブルシューティングします。

あなたが以前にこれに直面したことがあるなら、私と共有してください。

ご協力いただきありがとうございます。

2012年8月21日01:55(GMT + 1)更新

  1. mgilson、プロセス関数は、ネットワークシミュレータ2(NS2)トレースファイルを後処理するために使用されます。トレースファイルの一部の行は、以下のように共有されます。ワイヤレスネットワークのパフォーマンスを学習するために、Pythonスクリプトで多数のオブジェクト、カウンター、タプル、および辞書を使用しています。
s 1.231932886 _25_ AGT  --- 0 exp 10 [0 0 0 0 Y Y] ------- [25:0 0:0 32 0 0] 
s 1.232087886 _25_ MAC  --- 0 ARP 86 [0 ffffffff 67 806 Y Y] ------- [REQUEST 103/25 0/0]
r 1.232776108 _42_ MAC  --- 0 ARP 28 [0 ffffffff 67 806 Y Y] ------- [REQUEST 103/25 0/0]
r 1.232776625 _34_ MAC  --- 0 ARP 28 [0 ffffffff 67 806 Y Y] ------- [REQUEST 103/25 0/0]
r 1.232776633 _9_ MAC  --- 0 ARP 28 [0 ffffffff 67 806 Y Y] ------- [REQUEST 103/25 0/0]
r 1.232776658 _0_ MAC  --- 0 ARP 28 [0 ffffffff 67 806 Y Y] ------- [REQUEST 103/25 0/0]
r 1.232856942 _35_ MAC  --- 0 ARP 28 [0 ffffffff 64 806 Y Y] ------- [REQUEST 100/25 0/0]
s 1.232871658 _0_ MAC  --- 0 ARP 86 [13a 67 1 806 Y Y] ------- [REPLY 1/0 103/25]
r 1.233096712 _29_ MAC  --- 0 ARP 28 [0 ffffffff 66 806 Y Y] ------- [REQUEST 102/25 0/0]
r 1.233097047 _4_ MAC  --- 0 ARP 28 [0 ffffffff 66 806 Y Y] ------- [REQUEST 102/25 0/0]
r 1.233097050 _26_ MAC  --- 0 ARP 28 [0 ffffffff 66 806 Y Y] ------- [REQUEST 102/25 0/0]
r 1.233097051 _1_ MAC  --- 0 ARP 28 [0 ffffffff 66 806 Y Y] ------- [REQUEST 102/25 0/0]
r 1.233109522 _25_ MAC  --- 0 ARP 28 [13a 67 1 806 Y Y] ------- [REPLY 1/0 103/25]
s 1.233119522 _25_ MAC  --- 0 ACK 38 [0 1 67 0 Y Y] 
r 1.233236204 _17_ MAC  --- 0 ARP 28 [0 ffffffff 65 806 Y Y] ------- [REQUEST 101/25 0/0]
r 1.233236463 _20_ MAC  --- 0 ARP 28 [0 ffffffff 65 806 Y Y] ------- [REQUEST 101/25 0/0]
D 1.233236694 _18_ MAC  COL 0 ARP 86 [0 ffffffff 65 806 67 1] ------- [REQUEST 101/25 0/0]
  1. Heapyを使用して3レベルのプロファイリングを行う目的は、メモリの多くを消費しているオブジェクトを絞り込むのに役立つことです。ご覧のとおり、残念ながら、一般的すぎるため、どれを具体的に調整する必要があるのか​​わかりませんでした。例" dictof main .NodeStatistics"には36043(0.1%)オブジェクトのうち50オブジェクトしかありませんが、スクリプトの実行に使用される合計メモリの12%を占めているので、どの特定の辞書を見つけることができません。調べる必要があります。

  2. 以下のようにDavidEykの提案を実装してみました(スニペット)。500,000行ごとに手動でガベージコレクションを試みました。

import gc
  for i,line in enumerate(file(datafile)):
    if (i%500000==0):
      print '-----------This is line number', i
      collected = gc.collect()
      print "Garbage collector: collected %d objects." % (collected)

残念ながら、メモリ使用量はまだ3GBであり、出力(スニペット)は次のとおりです。

-----------This is line number 0
Garbage collector: collected 0 objects.
-----------This is line number 500000
Garbage collector: collected 0 objects.
  1. martineauの提案を実装すると、メモリ使用量が以前の3GBから22MBになっていることがわかります。達成するのを楽しみにしていたこと。奇妙なことは以下です、

以前と同じメモリプロファイリングを行いましたが、

レベル1プロファイリング

Partition of a set of 35474 objects. Total size = 5273376 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  15889  45  1283640  24   1283640  24 str
     1     50   0   628400  12   1912040  36 dict of __main__.NodeStatistics
     2   7559  21   617496  12   2529536  48 tuple
     3    781   2   589240  11   3118776  59 dict (no owner)
     4     90   0   278640   5   3397416  64 dict of module
     5   2132   6   255840   5   3653256  69 types.CodeType
     6   2059   6   247080   5   3900336  74 function
     7   1716   5   245408   5   4145744  79 list
     8    244   1   218512   4   4364256  83 type
     9    224   1   213632   4   4577888  87 dict of type
<104 more rows. Type e.g. '_.more' to view.>

以前のメモリプロファイリング出力を上記と比較すると、strは45オブジェクト(17376バイト)を削減し、tupleは25オブジェクト(3440バイト)を削減し、dict(所有者なし)はオブジェクトを変更しませんが、メモリサイズを1536バイト削減しました。main .NodeStatisticsのdictを含め、他のすべてのオブジェクトは同じです。オブジェクトの総数は35474です。オブジェクトのわずかな削減(0.2%)により、99.3%のメモリ節約が実現しました(3GBから22MB)。非常に奇妙な。

ご存知のように、メモリ不足が発生している場所はわかっていますが、出血の原因を特定することはできます。

これを調査し続けます。

すべてのポインタに感謝します。私は専門家ではないので、この機会を利用してPythonについて多くを学ぶことができます。私を助けるためにかかったあなたの時間を感謝します。

2012年8月23日更新00:01(GMT + 1)-解決済み

  1. martineauの提案に従って、最小限のコードを使用してデバッグを続けました。プロセス関数にコードを追加し、メモリの出血を観察し始めました。

  2. 以下のようにクラスを追加すると、メモリが出血し始めます。

class PacketStatistics(object):
    def __init__(self):
        self.event_id = 0
        self.event_source = 0
        self.event_dest = 0
        ...

136個のカウンターを持つ3つのクラスを使用しています。

  1. この問題について友人のグスタボ・カルネイロと話し合ったところ、彼はdictの代わりにスロットを使用することを提案しました。

  2. クラスを以下のように変換しました。

class PacketStatistics(object):
    __slots__ = ('event_id', 'event_source', 'event_dest',...)
    def __init__(self):
        self.event_id = 0
        self.event_source = 0
        self.event_dest = 0
        ...
  1. 3つのクラスすべてを変換すると、以前の3GBのメモリ使用量は504MBになりました。なんと80%のメモリ使用量節約!!

  2. 以下は、dictからslotへの変換後のメモリプロファイリングです。

Partition of a set of 36157 objects. Total size = 4758960 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  15966  44  1304424  27   1304424  27 str
     1   7592  21   624776  13   1929200  41 tuple
     2    780   2   587424  12   2516624  53 dict (no owner)
     3     90   0   278640   6   2795264  59 dict of module
     4   2132   6   255840   5   3051104  64 types.CodeType
     5   2059   6   247080   5   3298184  69 function
     6   1715   5   245336   5   3543520  74 list
     7    225   1   232344   5   3775864  79 dict of type
     8    244   1   223952   5   3999816  84 type
     9    166   0   190096   4   4189912  88 dict of class
<101 more rows. Type e.g. '_.more' to view.>

dict of __main__.NodeStatisticsもうトップ10に入っていません。

結果に満足しており、この問題を解決できてうれしいです。

ご指導ありがとうございます。本当に感謝しています。

rgdsサラバナンK

4

4 に答える 4

13
with open('datafile') as f:
    for line in f:
        process(line)

これが機能するのは、ファイルが、生成する行がなくなるまで一度に1行を生成するイテレータであるためです。

于 2012-08-20T14:10:22.820 に答える
4

このfileinputモジュールでは、ファイル全体をメモリにロードせずに、1行ずつ読み取ることができます。pydocs

import fileinput
for line in fileinput.input(['myfile']):
do_something(line)

yak.netから取得したコード例

于 2012-08-20T14:12:34.887 に答える
0

@mgilsonの答えは正しいです。単純な解決策には公式の言及があります(@HerrKaputtはコメントでこれに言及しました)

file = open('datafile')
for line in file:
    process(line)
file.close()

これは単純で、pythonicで、理解しやすいものです。動作がわからない場合は、withこれを使用してください。

他の投稿者が述べたように、これはfile.readlines()のような大きなリストを作成しません。むしろ、UNIXファイル/パイプに従来の方法で一度に1行ずつ引き出します。

于 2012-08-21T01:41:50.417 に答える
0

ファイルがJSON、XML、CSV、ゲノミクス、またはその他のよく知られた形式の場合、Cコードを直接使用し、ネイティブPythonでの解析よりも速度とメモリの両方ではるかに最適化された専用のリーダーがあります-可能な限りネイティブに解析することは避けてください。

しかし、一般的に、私の経験からのヒント:

  • Pythonのマルチプロセッシングパッケージは、サブプロセスの管理に最適です。サブプロセスが終了すると、すべてのメモリリークが解消されます。
  • リーダーサブプロセスをaとして実行し、amultiprocessing.Processを使用しmultiprocessing.Pipe(duplex=True)て通信します(ファイル名とその他の引数を送信してから、そのstdoutを読み取ります)
  • 小さい(ただし小さいわけではない)チャンク、たとえば64Kb-1Mbで読み取ります。メモリ使用量、および他の実行中のプロセス/サブプロセスとの応答性に優れています
于 2014-05-18T17:58:44.137 に答える