Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 高效地从非常大的二进制文件中读取几行_Python_Performance_Binaryfiles - Fatal编程技术网

Python 高效地从非常大的二进制文件中读取几行

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

下面是一个简单的例子来说明我的问题: 我有一个有1000万个值的大二进制文件

我想从该文件中的某些点获取5K值

我有一个索引列表,它给出了我的值所在的文件中的确切位置

为了解决这个问题,我尝试了两种方法:

  • 浏览这些值,然后简单地使用
    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)
        # ...