任何更快的选择?

时间:2010-06-10 04:44:03

标签: python performance

我必须从特定的行号读取文件,我知道行号说“n”: 我一直在考虑两种选择:

  1. for i in range(n):
        fname.readline()
    k=readline()
    print k
    
  2. i=0
    for line in fname:
        dictionary[i]=line
        i=i+1
    
  3. 但我想知道更快的替代方法,因为我可能必须在20000次不同的文件上执行此操作。 还有其他更好的选择吗?

    我甚至想知道是否有其他性能增强方法用于简单循环,因为我的代码具有嵌套循环

    感谢你

3 个答案:

答案 0 :(得分:5)

如果文件不是太大,标准库的linecache模块非常好 - 它可以让你直接询问这样一个文件的第N行。

如果文件 很大,我建议使用(警告,未经测试的代码):

def readlinenum(filepath, n, BUFSIZ=65536):
  bufs = [None] * 2
  previous_lines = lines_so_far = 0
  with open(filepath, 'b') as f
    while True:
      bufs[0] = f.read(BUFSIZ)
      if not bufs[0]:
        raise ValueError('File %s has only %d lines, not %d',
                         filepath, lines_so_far, n)
      lines_this_block = bufs[0].count('\n')
      updated_lines_count = lines_so_far + lines_this_block
      if n < updated_lines_count:
          break
      previous_lines = lines_so_far
      lines_so_far = updated_lines_count
      bufs[1] = bufs[0]
    if n == lines_so_far:
      # line split between blocks
      buf = bufs[1] + bufs[0]
      delta = n - previous_lines
    else:  # normal case
      buf = bufs[0]
      delta = n = lines_so_far
    f = cStringIO.StringIO(buf)
    for i, line in enumerate(f):
      if i == delta: break
    return line.rstrip()

一般的想法是在大文件中读取文件为二进制文件(至少与最长的行一样大) - 从二进制到“文本”的处理(在Windows上)对于大文件来说是昂贵的 - 并在大多数块上使用快速.count字符串方法。最后,我们可以在一个块上进行行解析(在异常情况下最多两个,其中所寻找的行跨越块边界)。

这种代码需要仔细测试和检查(我在这种情况下没有执行),容易出现一个一个和其他边界错误,所以我建议它只用于真正庞大的文件 - 如果使用linecache(它只是将整个文件吸收到内存而不是按块工作),那些本质上会破坏内存的那些内存。例如,在具有4GB字节RAM的典型现代机器上,我开始考虑用于超过GB或2的文本文件的这种技术。

编辑:评论者不相信读取文件的二进制文件比文本模式所需的处理速度快得多(仅限Windows)。为了说明这是多么错误,让我们使用'U'(“通用换行符”)选项来强制在Unix机器上进行行结束处理(因为我没有Windows机器来运行它; - )。使用通常的kjv.txt文件:

$ wc kjv.txt
  114150  821108 4834378 kjv.txt

(4.8 MB,114 Klines) - 大约是我之前提到的文件大小的1/1000:

$ python -mtimeit 'f=open("kjv.txt", "rb")' 'f.seek(0); f.read()'
100 loops, best of 3: 13.9 msec per loop
$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); f.read()'
10 loops, best of 3: 39.2 msec per loop

即,对于线端处理来说,这只是成本的3倍(这是在旧式笔记本电脑上,但比率在其他地方也应该是可重复的)。

当然,通过循环读取行甚至更慢:

$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0)' 'for x in f: pass'
10 loops, best of 3: 54.6 msec per loop

并使用readline作为评论提及(缓冲效率低于直接在文件上循环)最差:

$ python -mtimeit 'f=open("kjv.txt", "rU")' 'f.seek(0); x=1' 'while x: x=f.readline()'
10 loops, best of 3: 81.1 msec per loop

如果问题提到,有20,000个文件要读(比如说它们都是小的,按照kjv.txt的顺序),最快的方法(以二进制模式读取每个文件) (单根)需要大约260秒,4-5分钟,而最慢的(基于readline)应该花费大约1600秒,差不多半小时 - 对于许多人来说这是一个非常显着的差异,我会说大多数实际应用。

答案 1 :(得分:2)

除非你知道,或者可以弄清楚文件中n行的偏移量(例如,如果每行的宽度都是固定的),你必须读取行,直到你到达{ {1}}一个。

关于您的示例:

  • nxrange快,因为range必须生成列表,而range使用生成器
  • 如果您只需要行xrange,为什么要将所有行存储在字典中?

答案 2 :(得分:0)

缓存文件中每个行尾字符的偏移列表会花费大量内存,但每个内存页大约缓存一次(通常为4KB)会大大减少I / O和成本从已知偏移扫描几个KB可以忽略不计。因此,如果您的平均行长度为40个字符,则只需缓存文件中每100行结束的列表。您绘制线的确切位置取决于您拥有多少内存以及I / O的速度。您甚至可以通过缓存每个第1000行结尾字符的偏移列表而没有明显的性能差异而不是每个字符串的索引。