如何提高 Python 大文件的迭代性能

How to improve Python iteration performance over large files

我有一个大约 9,000 行的参考文件,其结构如下:(index, size) - 其中索引是唯一的,但大小可能不是。

0 193532
1 10508
2 13984
3 14296
4 12572
5 12652
6 13688
7 14256
8 230172
9 16076

我有一个大约 650,000 行的数据文件,其结构如下:(cluster, offset, size) - 其中 offset 是唯一的,但 size 不是。

446 0xdf6ad1 34572
447 0xdf8020 132484
451 0xe1871b 11044
451 0xe1b394 7404
451 0xe1d12b 5892
451 0xe1e99c 5692
452 0xe20092 6224
452 0xe21a4b 5428
452 0xe23029 5104
452 0xe2455e 138136

我需要比较参考文件第二列中的每个尺寸值与数据文件第三列中的尺寸值是否匹配。如果匹配,则输出偏移十六进制值(数据文件中的第二列)和索引值(参考文件中的第一列)。目前我正在使用以下代码执行此操作并将其通过管道传输到一个新文件:

#!/usr/bin/python3

import sys

ref_file = sys.argv[1]
dat_file = sys.argv[2]

with open(ref_file, 'r') as ref, open(dat_file, 'r') as dat:

    for r_line in ref:
        ref_size = r_line[r_line.find(' ') + 1:-1]

        for d_line in dat:
            dat_size = d_line[d_line.rfind(' ') + 1:-1]
            if dat_size == ref_size:
                print(d_line[d_line.find('0x') : d_line.rfind(' ')]
                      + '\t'
                      + r_line[:r_line.find(' ')])
        dat.seek(0)

典型的输出如下所示:

0x86ece1eb  0
0x16ff4628f 0
0x59b358020 0
0x27dfa8cb4 1
0x6f98eb88f 1
0x102cb10d4 2
0x18e2450c8 2
0x1a7aeed12 2
0x6cbb89262 2
0x34c8ad5   3
0x1c25c33e5 3

这工作正常,但对于给定的文件大小需要大约 50 分钟才能完成。

它已经完成了它的工作,但作为一名新手,我总是热衷于学习改进编码的方法并分享这些知识。我的问题是,我可以做哪些更改来提高这段代码的性能?

你可以执行以下操作,拿一本字典 dic 然后执行以下操作(以下是伪代码,我也假设大小不重复)

       for index,size in the first file:
           dic[size] = index
       for index,offset,size in second file:
          if size in dic.keys():
             print dic[size],offset

由于您通过 size 在文件中查找行,因此这些大小应该是任何字典数据结构中的键。这本字典你需要摆脱嵌套循环,它是这里真正的性能杀手。此外,由于您的大小不是唯一的,您将不得不使用 offset / index 值的列表(取决于您要存储在字典中的文件)。 defaultdict 将帮助您避免一些笨拙的代码:

from collections import defaultdict

with open(ref_file, 'r') as ref, open(dat_file, 'r') as dat:
    dat_dic = defaultdict(list)  # maintain a list of offsets for each size
    for d_line in dat:
        _, offset, size = d_line.split()
        dat_dic[size].append(offset)

    for r_line in ref:
        index, size = r_line.split()
        for offset in dat_dic[size]:  
            # dict lookup is O(1) and not O(N) ...
            # ... as looping over the dat_file is
            print('{offset}\t{index}'.format(offset=offset, index=index))

如果你的输出行的顺序不重要,你可以考虑反过来做,因为你的 dat_file 太大了,因此从它构建 defaultdict 使用了很多更多内存。