簡體   English   中英

提高從二進制文件讀取和轉換的速度?

[英]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,這會產生一些非常煩人的延遲(特別是考慮到我試圖鏡像的另一個基准共享軟件程序加載文件方式更快)。

我想知道的:

  1. 如果有一些違反“良好實踐”的行為:錯誤安排的循環、花費比必要時間更長的重復操作、使用低效的容器類型(字典?)等。
  2. 如果這個讀取速度正常,或者Python正常,如果讀取速度正常
  3. 如果創建 C++ 編譯擴展可能會提高性能,並且如果這將是推薦的方法。
  4. (當然)如果有人建議對這段代碼進行一些修改,最好是基於以前類似操作的經驗。

謝謝閱讀

(我已經發了幾個關於我這個工作的問題,希望它們都是概念上無關的,也希望不要太重復。)

編輯: 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相比,速度提升非常令人印象深刻。

您可以使用array來讀取數據:

import array
import os

fn = 'data.bin'
a = array.array('h')
a.fromfile(open(fn, 'rb'), os.path.getsize(fn) // a.itemsize)

它比struct.unpack 的答案中的 struct.unpack 快 40 倍。

如果文件只有 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.

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