1

我有一个非常大的文本文件(27GB),我试图通过删除在第二个数据库中重复的行来缩小它,该数据库有几个更合理大小的文件(500MB-2GB)。我有一些功能代码,我想知道有什么方法可以优化此代码以更快地运行,人类时钟时间?目前,在一个 1.5GB 输入和 500MB 过滤器的小型测试运行中,这需要 75~ 秒才能完成。

我已经对这个想法进行了多次迭代,这个目前是最好的,如果有人有想法为过滤器制作更好的逻辑结构,我很想听听,过去的尝试都比这个更糟糕:将过滤器加载到一个集合中并在输入中循环搜索重复项(大约是这个速度的一半),将输入加载到一个集合中并通过差异更新运行过滤器(几乎和这个一样快,但也做相反的事情我想要),并将输入和过滤器分块加载到集合中并进行集合差异(这是一个可怕的,可怕的想法,如果我的过滤器更小,那么我也不必拆分它们。 )

所以,这些都是我尝试过的所有事情。所有这些进程都在 CPU 上发挥最大作用,我的最终版本以大约 25-50% 的磁盘 I/O 运行,过滤器和输出在一个物理磁盘上,输入在另一个物理磁盘上。我正在运行双核,不知道这个特定的脚本是否可以线程化,以前从未做过任何多线程处理,所以如果有可能,我很想指出正确的方向。

有关数据的信息!如前所述,输入比滤波器大很多倍。我预计重复的比例很小。数据以行为单位,所有行的长度都小于 20 个 ASCII 字符。文件全部排序。

我已经改变了三个逻辑语句的顺序,基于唯一输入行将是大多数行的期望,然后是唯一过滤器,然后是重复,在完全没有重复的“最佳”情况下,节省了我大约 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

您可以通过执行每行仅执行一次字符串比较操作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 回答
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 回答
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 回答