簡體   English   中英

讀取大型二進制文件(超過 500 MB)的最快方法?

[英]Fastest Way to Read Large Binary Files (More than 500 MB)?

我想讀取大型二進制文件並分成 6 個字節的塊。 例如,現在我可以在 82 秒內讀取 1GB 的二進制文件,但是速度太慢了。 達到最大速度的最佳方法是什么?

請注意,我不能使用struct 因為我選擇的塊不是 2 的冪(6 個字節)。

with open(file, "rb") as infile:
     data_arr = []
     start = time()
     while True:
         data = infile.read(6)
         if not data: break
         data_arr.append(data)

你有幾個不同的選擇。 您的主要問題是,由於塊很小(6 字節),在獲取塊和垃圾收集方面花費了大量開銷。

有兩種主要方法可以解決這個問題:

  1. 將整個文件加載到內存中,然后將其分成塊。 這是最快的方法,但您的文件越大,您就越有可能開始遇到 MemoryErrors。

  2. 一次將一個塊加載到內存中,對其進行處理,然后移至下一個塊。 總體而言,這並沒有更快,但可以預先節省時間,因為您無需等待整個文件加載完畢即可開始處理。

  3. 試驗 1. 和 2. 的組合(以大塊緩沖文件並將其分成較小的塊,以塊大小的倍數加載文件等)。 這留給查看者作為練習,因為需要大量的實驗才能獲得快速正確運行的代碼。

一些代碼,時間比較:

import timeit


def read_original(filename):
    with open(filename, "rb") as infile:
        data_arr = []
        while True:
            data = infile.read(6)
            if not data:
                break
            data_arr.append(data)
    return data_arr


# the bigger the file, the more likely this is to cause python to crash
def read_better(filename):
    with open(filename, "rb") as infile:
        # read everything into memory at once
        data = infile.read()
        # separate string into 6-byte chunks
        data_arr = [data[i:i+6] for i in range(0, len(data), 6)]
    return data_arr

# no faster than the original, but allows you to work on each piece without loading the whole into memory
def read_iter(filename):
    with open(filename, "rb") as infile:
        data = infile.read(6)
        while data:
            yield data
            data = infile.read(6)


def main():
    # 93.8688215 s
    tm = timeit.timeit(stmt="read_original('test/oraociei12.dll')", setup="from __main__ import read_original", number=10)
    print(tm)
    # 85.69337399999999 s
    tm = timeit.timeit(stmt="read_better('test/oraociei12.dll')", setup="from __main__ import read_better", number=10)
    print(tm)
    # 103.0508528 s
    tm = timeit.timeit(stmt="[x for x in read_iter('test/oraociei12.dll')]", setup="from __main__ import read_iter", number=10)
    print(tm)

if __name__ == '__main__':
    main()

這種方式要快得多。

import sys
from functools import partial

SIX = 6
MULTIPLIER = 30000
SIX_COUNT = SIX * MULTIPLIER

def do(data):
    for chunk in iter(partial(data.read, SIX_COUNT), b""):
        six_list = [ chunk[i:i+SIX] for i in range(0, len(chunk), SIX) ]

if __name__ == "__main__": 
    args = sys.argv[1:]
    for arg in args:
        with open(arg,'rb') as data:
            do(data)

暫無
暫無

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

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