1

非常に大きなテキストファイル(27GB)が1つあり、より適切なサイズ(500MB-2GB)のファイルが複数ある2番目のデータベースで重複している行を削除して小さくしようとしています。私はいくつかの関数型コードを持っていますが、私が疑問に思っているのは、このコードを最適化してより高速な人間の時計時間を実行する方法はありますか?現時点では、1.5GBの入力と500MBのフィルターを使用した小規模なテスト実行では、これが完了するまでに75秒かかります。

私はこのアイデアを何度も繰り返してきましたが、これは現在のところ最適です。誰かがフィルターのより良い論理構造を作成するためのアイデアを持っているなら、それを聞きたいです。過去の試みはすべてこれよりも悪いものでした。 :フィルターをセットにロードし、入力を循環して重複を検索し(これの約半分の速度)、入力をセットにロードし、difference_updateを介してフィルターを実行します(これとほぼ同じ速度ですが、逆のことも行います)私が欲しかったのは)、入力とフィルターの両方をチャンクのセットにロードし、セットの違いを実行することです(これは、フィルターが小さければ(多分?)うまくいくので、それらを分割する必要もありませんでした。 )。

だから、これらは私が試したすべてのものです。これらのプロセスはすべてCPUで最大になり、最終バージョンは約25〜50%のディスクI / Oで実行され、フィルターと出力は1つの物理ディスクにあり、入力は別の物理ディスクにあります。私はデュアルコアを実行していますが、この特定のスクリプトをスレッド化できるかどうかわかりません。これまでマルチスレッド化を行ったことはありません。その可能性がある場合は、正しい方向に向けてください。

データに関する情報!前に述べたように、入力はフィルターより何倍も大きくなります。重複の割合はごくわずかだと思います。データは行単位であり、すべて20ASCII文字未満の長さです。ファイルはすべてソートされています。

一意の入力行が行の大部分、次に一意のフィルター、次に重複するという予想に基づいて、3つの論理ステートメントの順序を既に変更しました。これは、重複がまったくない「最良の」場合です。約10%の時間を節約できました。

助言がありますか?

def sortedfilter(input,filter,output):
    file_input = open(input,'r')
    file_filter = open(filter,'r')
    file_output = open(output,'w')
    inline = file_input.next()
    filterline = file_filter.next()
    try:
        while inline and filterline:
            if inline < filterline:
                file_output.write(inline)
                inline = file_input.next()
                continue
            if inline > filterline:
                filterline = file_filter.next()
                continue
            if inline == filterline:
                filterline = file_filter.next()
                inline = file_input.next()
    except StopIteration:
        file_output.writelines(file_input.readlines())
    finally:
        file_filter.close()
        file_input.close()
        file_output.close()
4

4 に答える 4

1

これがどのように比較されるかを知りたいと思います。基本的には、反復の順序で少し遊んでいるだけです。

def sortedfilter(in_fname, filter_fname, out_fname):
    with open(in_fname) as inf, open(filter_fname) as fil, open(out_fname, 'w') as outf:
        ins = inf.next()
        try:
            for fs in fil:
                while ins < fs:
                    outf.write(ins)
                    ins = inf.next()
                while ins == fs:
                    ins = inf.next()
        except StopIteration:
            # reached end of inf before end of fil
            pass
        else:
            # reached end of fil first, pass rest of inf through
            file_output.writelines(file_input.readlines())
于 2012-07-07T20:10:36.290 に答える
1

次のコマンドを実行すると、文字列比較操作を1行に1回だけ実行できますcmp(inline, filterline)

  • -1意味inline < filterline
  • 0意味inline == filterline
  • +1を意味しinline < filterlineます。

これにより、さらに数パーセント増える可能性があります。だから次のようなもの:

    while inline and filterline:
        comparison = cmp(inline, filterline)
        if comparison == -1:
            file_output.write(inline)
            inline = file_input.next()
            continue
        if comparison == 1:
            filterline = file_filter.next()
            continue
        if comparison == 0:
            filterline = file_filter.next()
            inline = file_input.next()
于 2012-07-07T19:57:01.693 に答える
1

入力ファイルがソートされていることを確認すると、次のように動作するはずです。heapq のマージは、groupby が動作するソートされた入力からソートされたストリームを生成します。長さが 1 より大きいグループは破棄されます。このストリームベースのアプローチでは、メモリ要件が比較的低くなります。

from itertools import groupby, repeat, izip
from heapq import merge
from operator import itemgetter

with open('input.txt', 'r') as file_input, open('filter.txt', 'r') as file_filter, open('output.txt', 'w') as file_output:
  file_input_1 = izip(file_input, repeat(1))
  file_filter_1 = izip(file_filter, repeat(2))
  gen = merge(file_input_1, file_filter_1)
  gen = ((k, list(g)) for (k, g) in groupby(gen, key=itemgetter(0)))
  gen = (k for (k, g) in gen if len(g) == 1 and g[0][1]  == 1)
  for line in gen:
    file_output.write(line)
于 2012-07-07T20:07:56.217 に答える
0

Unicode に変換する必要がないように、ファイルをバイナリとして開くことを検討してください。

with open(in_fname,'rb') as inf, open(filter_fname,'rb') as fil, open(out_fname, 'wb') as outf:
于 2012-07-07T20:59:19.387 に答える