[英]Improve speed of reading and converting from binary file?
我知道之前有一些關於文件讀取、二進制數據處理和 integer 轉換使用struct
的問題,所以我來這里詢問一段我認為運行時間過長的代碼。 正在讀取的文件是多通道數據樣本記錄(短整數),具有插入的數據間隔(因此嵌套for
語句)。 代碼如下:
# channel_content is a dictionary, channel_content[channel]['nsamples'] is a string
for rec in xrange(number_of_intervals)):
for channel in channel_names:
channel_content[channel]['recording'].extend(
[struct.unpack( "h", f.read(2))[0]
for iteration in xrange(int(channel_content[channel]['nsamples']))])
使用此代碼,我使用具有 2Mb RAM 的雙核讀取每兆字節 2.2 秒,而我的文件通常有 20+ Mb,這會產生一些非常煩人的延遲(特別是考慮到我試圖鏡像的另一個基准共享軟件程序加載文件方式更快)。
我想知道的:
謝謝閱讀
(我已經發了幾個關於我這個工作的問題,希望它們都是概念上無關的,也希望不要太重復。)
編輯: channel_names
是一個列表,所以我做了@eumiro 建議的更正(刪除拼寫錯誤的括號)
編輯:我目前正在接受 Sebastian 的建議,即使用帶有fromfile()
方法的array
,並且很快會將最終代碼放在這里。 此外,每一個貢獻都對我非常有用,我很高興地感謝每一位好心回答的人。
使用array.fromfile()
一次后的最終形式,然后通過切片大數組為每個通道交替擴展一個數組:
fullsamples = array('h')
fullsamples.fromfile(f, os.path.getsize(f.filename)/fullsamples.itemsize - f.tell())
position = 0
for rec in xrange(int(self.header['nrecs'])):
for channel in self.channel_labels:
samples = int(self.channel_content[channel]['nsamples'])
self.channel_content[channel]['recording'].extend(
fullsamples[position:position+samples])
position += samples
與一次讀取文件或以任何形式使用struct
相比,速度提升非常令人印象深刻。
如果文件只有 20-30M,為什么不讀取整個文件,在一次調用中解碼 nums 以unpack
,然后通過遍歷數組將它們分發到您的通道中:
data = open('data.bin', 'rb').read()
values = struct.unpack('%dh' % len(data)/2, data)
del data
# iterate over channels, and assign from values using indices/slices
快速測試表明,這導致struct.unpack('h', f.read(2))
在 20M 文件上的速度提高了 10 倍。
單個數組 fromfile 調用絕對是最快的,但如果數據序列與其他值類型交錯,則不會工作。
在這種情況下,可以與前面的 struct 答案相結合的另一大速度提升是,與其多次調用解包 function ,不如使用每個塊的格式預編譯一個 struct.Struct object 。 從文檔:
一次創建 Struct object 並調用其方法比調用具有相同格式的結構函數更有效,因為格式字符串只需要編譯一次。
例如,如果你想一次解開 1000 個交錯的短褲和浮點數,你可以這樣寫:
chunksize = 1000
structobj = struct.Struct("hf" * chunksize)
while True:
chunkdata = structobj.unpack(fileobj.read(structobj.size))
(請注意,該示例只是部分示例,需要考慮更改文件末尾的塊大小並中斷 while 循環。)
不確定它是否會更快,但我會嘗試解碼大塊單詞而不是一次解碼一個單詞。 例如,您可以一次讀取 100 個字節的數據,例如:
s = f.read(100)
struct.unpack(str(len(s)/2)+"h", s)
extend()接受可迭代對象,也就是說,您可以編寫.extend(...)
代替.extend([...])
) 。 它可能會加速程序,因為extend()將在生成器上處理,而不是在構建列表上
您的代碼中存在不連貫性:您首先定義channel_content = {}
,然后執行channel_content[channel]['recording'].extend(...)
,這需要初步存在一個關鍵通道和一個子鍵'記錄' ,以列表作為值,以便能夠擴展到某些東西
self.channel_content[channel]['nsamples']
的本質是什么,以便可以提交給int() function?
number_of_intervals來自哪里? 間隔的性質是什么?
在rec in xrange(number_of_intervals)):
,我不再看到rec 。 因此,在我看來,您正在for channel in channel_names:
與number_of_intervals表示的數字一樣多。 在 f 中是否有 number_of_intervals * int(self.channel_content[channel]['nsamples']) * 2 值要讀取?
我在文檔中讀到:
class struct.Struct(格式)
返回一個新的 Struct object 根據格式字符串格式寫入和讀取二進制數據。 一次創建 Struct object 並調用其方法比調用具有相同格式的結構函數更有效,因為格式字符串只需要編譯一次。
這表達了與樣本偏差相同的想法。
如果您的目標是創建字典,也可以使用帶有生成器的dict()作為參數
.
我提議
channel_content = {}
for rec in xrange(number_of_intervals)):
for channel in channel_names:
N = int(self.channel_content[channel]['nsamples'])
upk = str(N)+"h", f.read(2*N)
channel_content[channel]['recording'].extend(struct.unpack(x) for i,x in enumerate(upk) if not i%2)
我不知道如何考慮 JF Sebastian 建議使用數組
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.