Python 高效地从非常大的二进制文件中读取几行
下面是一个简单的例子来说明我的问题: 我有一个有1000万个值的大二进制文件 我想从该文件中的某些点获取5K值 我有一个索引列表,它给出了我的值所在的文件中的确切位置 为了解决这个问题,我尝试了两种方法:Python 高效地从非常大的二进制文件中读取几行,python,performance,binaryfiles,Python,Performance,Binaryfiles,下面是一个简单的例子来说明我的问题: 我有一个有1000万个值的大二进制文件 我想从该文件中的某些点获取5K值 我有一个索引列表,它给出了我的值所在的文件中的确切位置 为了解决这个问题,我尝试了两种方法: 浏览这些值,然后简单地使用seek()(从文件的开头)获取每个值,如下所示: binaryFile_new = open(binary_folder_path, "r+b") for index in index_list: binaryFile_new.seek (size * (i
seek()
(从文件的开头)获取每个值,如下所示:
binaryFile_new = open(binary_folder_path, "r+b")
for index in index_list:
binaryFile_new.seek (size * (index), 0)
wanted_line = binaryFile_new.read (size)
wanted_line_list.append(wanted_line)
binaryFile_new.close()
但据我所知,该解决方案从一开始就读取每个索引,因此,就文件大小而言,复杂性为O(N**2)binaryFile_new = open(binary_folder_path, "r+b")
sorted_index_list = sorted(index_list)
for i, index in enumerate(sorted_index_list):
if i == 0:
binaryFile_new.seek (size * (v), 0)
else:
binaryFile_new.seek ((index - sorted_index_list[i-1]) * size - size, 1)
binaryFile_new.seek (size * (index), 0)
wanted_line = binaryFile_new.read (size)
wanted_line_list.append(wanted_line)
binaryFile_new.close()
我希望第二个解决方案会快得多,因为理论上,它将遍历整个文件一次O(N)
但由于某些原因,两种解决方案运行相同mmap
软件包会有所帮助?不过,我认为mmap
也会扫描整个文件,直到它到达索引,所以它不是“真实”随机访问。我会选择#1:
(我对代码进行了一些清理,以符合Python命名约定,并避免使用神奇的0
常量,因为SEEK\u SET
是默认值。)
据我所知,这个解决方案从一开始就读取每个索引,因此就文件大小而言,复杂性为O(N**2)
不,一个seek()。从文件开始到文件结束的搜索成本大致相同
对索引进行排序,以便在从当前位置搜索时“一次”浏览文件
我不能很快找到这方面的参考,但我相信,为了使用SEEK\u CUR而不是SEEK\u SET,计算相对偏移量是毫无意义的
如果只是按顺序而不是随机地查找所需的位置,可能会有一个小的改进,因为从缓存中服务随机读取的可能性会增加,以防您需要读取的许多点恰好彼此接近(因此您的读取模式会触发文件系统中的提前读取)
也许mmap软件包会有所帮助?尽管如此,我认为mmap也会扫描整个文件,直到它到达索引,所以它不是“真正的”随机访问
mmap不会扫描该文件。它在程序的虚拟内存中设置了一个与文件相对应的区域,因此第一次从该区域访问任何页面都会导致页面错误,在此期间,操作系统从文件中读取该页面(数KB)(假设它不在页面缓存中),然后让程序继续
互联网上充斥着关于read
与mmap
的相对优点的讨论,但我建议您利用这段时间来了解和
[编辑]如果您需要读取的许多值都在同一块中(这不是给定的),那么以大于值的大小的块读取可能会为您节省一点CPU时间,但除非您的程序在生产中受CPU限制,否则我也不会为此烦恼。如果您将其分块,则可能会重复(就像在dupe目标中一样)然后一次只加载一个区块。选择一个有意义的区块大小(并可能添加用于恢复跨多个区块的数据的逻辑),您将永远不会接近RAM限制。我认为mmap
模块会有所帮助,因为它的实现将使用操作系统的内存分页(可能非常有效)有效地进行任何“寻找”需要。您好@Nickolay,谢谢您的详细回答。我有限的理解来自我之前的问题和一些社区成员的电子邮件,我无法找到任何关于seek的容易获得的详细信息而不深入细节。所以我只是想这就是为什么它在较大的文件中会变慢。关于问题本身,我怀疑实际的问题是缓存和页面加载。当我运行脚本时,它从几个文件加载了一堆值,然后它只是挂起几秒钟,这个循环重复。这感觉就像一些内存被填满了,然后python/内核需要一些时间清空缓冲区以读取/加载新页面。我希望加载值该文件可能会有帮助,但显然没有。在我当前的状态下,所有的值都分散在整个文件中。如果我找不到任何解决方案,我将需要重写数据以将值分组在一起。因为“问题本身”在另一个问题中进行了描述,我也在那里发布了一个答案。不应该挂起,但似乎您希望您的磁盘能像RAM一样工作。
for index in index_list:
binary_file.seek(size * index)
# ...