簡體   English   中英

從一個非常大的二進制文件中有效地讀取幾行

[英]Efficiently reading few lines from a very large binary file

這是一個簡單的例子來說明我的問題:我有一個包含1000萬個值的大型二進制文件。

我想從這個文件中的某些點獲得5K值。

我有一個索引列表,為我提供了我有價值的文件中的確切位置。

為了解決這個問題,我試過兩種方法

  1. 通過值並簡單地使用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)。

  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:

for index in index_list:
    binary_file.seek(size * index)
    # ...

(我清理了你的代碼以符合Python命名約定並避免使用魔術0常量,因為無論如何SEEK_SET都是默認值。)

據我所知,這個解決方案從頭開始讀取每個索引,因此就文件大小而言,復雜度為O(N ** 2)。

不, seek()不會“從頭開始閱讀”,這會破壞尋求的目的。 尋找文件的開頭和文件的末尾具有大致相同的成本。

對索引進行排序,以便在從當前位置搜索時可以“一次”查看文件

我無法快速找到這方面的參考,但我相信,為了使用SEEK_CUR而不是SEEK_SET,計算相對偏移絕對沒有意義。

可能會有一個小小的改進,只需要按順序尋找你需要的位置,而不是隨機,因為你需要從緩存中提供隨機讀取的機會增加,以防你需要讀取的許多點碰巧接近每個其他(因此您的讀取模式會在文件系統中觸發預讀)。

也許mmap包會有幫助嗎? 雖然,我認為mmap也掃描整個文件,直到它到達索引,因此它不是“真正的”隨機訪問。

mmap不掃描文件。 它在程序的虛擬內存中設置一個與文件對應的區域,這樣第一次訪問該區域的任何頁面都會導致頁面錯誤,在此期間操作系統從文件中讀取該頁面(幾KB)(假設它是讓程序繼續之前,不要在頁面緩存中)

互聯網上充斥着關於read vs mmap的相對優點的討論,但我建議你不要試圖通過使用mmap進行優化,並利用這段時間來了解虛擬內存頁面緩存

[edit]以大於值的size讀取塊可能會節省一些CPU時間,以防需要讀取的許多值位於同一塊(不是給定的塊)中 - 但除非您的程序是CPU在生產中,我也不會打擾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM