2016-06-28 15 views
3

何らかの理由で1行しかない巨大な(250 MB)テキストファイルを解析しなければならないため、試したすべてのテキストエディタ(Notepad ++、Visual Studio、Matlab)がロードに失敗します。したがって、私はそれを1枚ずつ読んで、そして(#で始まる)論​​理行が完全に読まれるたびにそれを解析:予期せぬパフォーマンスの低下

f = open(filename, "rt") 

line = "" 
buffer = "blub" 
while buffer != "": 
    buffer = f.read(10000) 
    i = buffer.find('#') 
    if i != -1: # end of line found 
     line += buffer[:i] 
     ProcessLine(line) 
     line = buffer[i+1:] # skip the '#' 
    else: # still reading current line 
     line += buffer 

これは合理的にうまく機能、しかし、ラインが私のバッファよりも短いこと、起こるかもしれませんこれは私にラインをスキップさせるでしょう。だから私はループをループに置き換えました。

while buffer != "": 
    buffer = f.read(10000) 
    i = buffer.find('#') 
    while i != -1: 
     pixels += 1 
     line += buffer[:i] 
     buffer = buffer[i+1:] 
     ProcessLine(line) 
     i = buffer.find('#') 
    line += buffer 

これはやっています。しかし、これは少なくとも100倍遅く、大きなファイルを読むのに無用です。私は実際には見えません、どうしてこのようなことが起こるか、私は内部のループを持っていますが、ほとんどの場合、それは一度だけ繰り返されます。また、私はおそらく、パフォーマンスが半減したらどういうわけか理解できたバッファー(buffer = buffer[i+1:])をコピーしていますが、これがどのようにして100倍遅くなるかわかりません。

注:私の(論理)行は約27.000バイトです。したがって、私のバッファが10.000バイトであれば、最初の実装で行をスキップすることは決してありません。しかし、これはパフォーマンスに影響を与えないように見えますが、第2実装の内部ループが最大で1回評価されても、パフォーマンスはまだひどいです。

私は見逃しているボンネットの下で何が起こっていますか?

+1

最初のバージョンでは、10.000に1回だけ分割します。2番目の分割では、何回もチャンクに入るので、Xは10.000あたりの平均数です。バイト。それがX倍遅いなら、あなたの答えはここにあります。 – Andrey

+0

いいえ、それはできません。私のテストケースでは、#〜27.000文字毎に#があります。バッファの長さが30.000の場合、これらのうちのいくつかをスキップします。10.000の場合は、すべて取得します。どちらの場合も、2番目の実装でのパフォーマンスは恐ろしいものです。私はこれをメモとして加えました。 – JonathanK

+0

250MBは少量のデータです。なぜあなたはそれをすべてメモリに読んでそこで処理しないのですか? OSファイルの読み込みバッファを使用しても、はるかに高速になります。 – advance512

答えて

2

私はあなたが何をしたいのかを正しく理解している場合、あなたのコードの両方のバージョンが間違っているよりも。 @ Leonのように、第二版ではProcessLine(line)の後にline = ""がなくなっていて、最初の行だけが正解であり、行がバッファより短いと悲しいように、line += buffer[:i]にバッファの最初の部分を使用しますこの行にline = buffer[i+1:]と入力して、lineの文字数が1000文字、bufferの文字数が10000文字の場合は、line += buffer[:i]を使用すると、1行に9000文字が含まれている可能性があります。読書から:

は、「これは合理的にうまく機能、しかし、私がラインをスキップする原因と思われる、ライ​​ンが私のバッファよりも短いこと、起こるかもしれない」

私はあなたがいることが、その理由を実現したと思います私は詳細に書いています、それはあなたの最初のバージョンがより速く働く理由です。

f = open('textfile.txt', "rt") 
buffer = f.read() 
f.close() 
l = buffer.split('#') 

とあなたは同じようなものを使用することができますより:

ので、あなたのコードは次のようになり、私は最高の穴ファイルと行を取得するには、その後、分割テキストを読むことであろうと思う、と説明した後

for line in l: 
    ProcessLine(line) 

リストを取得するにはl 2秒以下でした。

PS:メモ帳で大きなファイル(250MBなど)を開く際に問題はありません。500MBのファイルも開いていました。

+0

私は問題は、すべてがちょうど1行にあると思います。 '#'の後に改行を作成して保存すると、メモ帳でそれを読むことができます。問題はありません。 同様に、 'for l in open(filename)'だけを実行してもうまくいきません。しかし、あなたの 'read()'と 'split()'を使った解決策は、これまでのやり方よりも素晴らしく簡単です。 – JonathanK

1

2番目のバージョンでは動作速度が遅いだけでなく、正しく動作しません。

最初のバージョンではlineを割り当て(line = buffer[i+1:])にリセットしましたが、2番目のバージョンではlineにのみ追加します。その結果、2番目のバージョンでは、最後にlineには、ファイルの内容全体が#シンボル以下含まれています。

はすぐにそれを処理した後lineをクリアすることで、コードを修正:

while buffer != "": 
    buffer = f.read(10000) 
    i = buffer.find('#') 
    while i != -1: 
     pixels += 1 
     line += buffer[:i] 
     buffer = buffer[i+1:] 
     ProcessLine(line) 
     line = ""    # sic! 
     i = buffer.find('#') 
    line += buffer 
+0

あなたは正しいです。そしてこれは基本的に実行時にアルゴリズムを二次関数にします。これは 'line'の始まりが何度も何度も解析されるためです。したがって、パフォーマンスが大幅に低下していることを完全に説明しています。 – JonathanK