2017-10-11 80 views
0

我有一個巨大的二進制文件(幾個GB)具有以下DATAFORMAT:快速閱讀和解釋二進制文件

4個後續字節構成一個複合數據點(32位),其由以下組成:

b0-b3 4 flag bits 
b4-b17 14 bit signed integer 
b18-b32 14 bit signed integer 

我需要分別訪問帶符號整數和標誌位,並附加到列表或一些更智能的數據結構(尚未決定)。目前我使用下面的代碼來閱讀:

from collections import namedtuple 
DataPackage = namedtuple('DataPackage', ['ie', 'if1', 'if2', 'if3', 'quad2', 'quad1']) 
def _unpack_integer(bits): 
    value = int(bits, 2) 
    if bits[0] == '1': 
     value -= (1 << len(bits)) 
    return value 


def unpack(data): 
    bits = ''.join(['{0:08b}'.format(b) for b in bytearray(data)]) 
    flags = [bool(bits[i]) for i in range(4)] 
    quad2 = _unpack_integer(bits[4:18]) 
    quad1 = _unpack_integer(bits[18:]) 
    return DataPackage(flags[0], flags[1], flags[2], flags[3], quad2, quad1) 

def read_file(filename, datapoints=None): 
    data = [] 
    i = 0 
    with open(filename, 'rb') as fh: 
     value = fh.read(4) 
     while value: 
      dp = unpack(value) 
      data.append(dp) 
      value = fh.read(4) 
      i += 1 
      if i % 10000 == 0: 
       print('Read: %d kB' % (float(i) * 4.0/1000.0)) 
      if datapoints: 
       if i == datapoints: 
        break 
    return data 

if __name__ == '__main__': 
    data = read_heterodyne_file('test.dat') 

此代碼的工作,但它是我的目的,(2秒爲10萬個數據點與4字節每個)太慢。至少我需要10倍的速度。

分析器說,代碼花費的時間大部分是字符串格式(獲取位)和_unpack_integer()。

不幸的是我不知道如何在這裏繼續。我正在考慮使用cython或直接編寫一些c代碼來完成讀取。我也嘗試過Pypy ant,它給了我巨大的性能提升,但不幸的是,它需要兼容一個更大的項目,它不能與Pypy一起工作。

+2

刪除格式並直接在讀取值上使用掩碼。跳過「轉換爲字符串以獲取位」階段。 –

+0

謝謝。這似乎很有道理。所以得到quad2我需要沿着行數據= 00001111111111111100000000000000,然後我不知道如何將其轉換爲int16 – dreichler

+0

爲了嚴格起見,1 kB有** 1024 ** B(不是1000)。 – CristiFati

回答

1

如果您已經有了識別數據結構的c/C++庫,我會推薦嘗試ctypes。好處是,數據結構仍然可用於您的Python,而「加載」將會很快。如果你已經有一個C庫來加載數據,你可以使用該庫中的函數調用來完成繁重的工作,並將數據映射到Python結構中。對不起,我無法嘗試併爲您的示例(可能是別人的手杖)提供正確的代碼,但這裏有一些提示可以幫助您開始創建

我想知道如何創建位向量在Python中: https://stackoverflow.com/a/40364970/262108

我上面提到的方法適用於您描述的類似問題。在這裏,我使用的ctypes創建一個ctypes數據結構(從而使我使用的對象的任何其他蟒物體),同時還能夠通過它沿一個C庫:

https://gist.github.com/lonetwin/2bfdd41da41dae326afb

+0

這是一個很好的提示,謝謝。我很快就會嘗試在C中做到這一點,我可以獲得比我的回答更多的表現。 – dreichler

1

由於Jean-FrançoisFabre I的提示發現了一個合適的使用位掩的方法,與問題中的代碼相比,我可以提高因子6的速度。它現在有大約300k個數據點。

另外我忽略了使用公認的很好的命名元組,並用列表替換它,因爲我發現這也是一個瓶頸。

代碼現在看起來像

masks = [2**(31-i) for i in range(4)] 
def unpack3(data): 
    data = struct.unpack('>I', data)[0] 
    quad2 = (data & 0xfffc000) >> 14 
    quad1 = data & 0x3fff 
    if (quad2 & (1 << (14 - 1))) != 0: 
     quad2 = quad2 - (1 << 14) 
    if (quad1 & (1 << (14 - 1))) != 0: 
     quad1 = quad1 - (1 << 14) 
    flag0 = data & masks[0] 
    flag1 = data & masks[1] 
    flag2 = data & masks[2] 
    flag3 = data & masks[3] 
    return flag0, flag1, flag2, flag3, quad2, quad1 

行探查說:

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    58           @profile 
    59           def unpack3(data): 
    60 1000000  3805727  3.8  12.3  data = struct.unpack('>I', data)[0] 
    61 1000000  2670576  2.7  8.7  quad2 = (data & 0xfffc000) >> 14 
    62 1000000  2257150  2.3  7.3  quad1 = data & 0x3fff 
    63 1000000  2634679  2.6  8.5  if (quad2 & (1 << (14 - 1))) != 0: 
    64 976874  2234091  2.3  7.2   quad2 = quad2 - (1 << 14) 
    65 1000000  2660488  2.7  8.6  if (quad1 & (1 << (14 - 1))) != 0: 
    66 510978  1218965  2.4  3.9   quad1 = quad1 - (1 << 14) 
    67 1000000  3099397  3.1  10.0  flag0 = data & masks[0] 
    68 1000000  2583991  2.6  8.4  flag1 = data & masks[1] 
    69 1000000  2486619  2.5  8.1  flag2 = data & masks[2] 
    70 1000000  2473058  2.5  8.0  flag3 = data & masks[3] 
    71 1000000  2742228  2.7  8.9  return flag0, flag1, flag2, flag3, quad2, quad1 

所以沒有一個明確的瓶頸了。現在它可能會像純Python一樣快。還是有人有進一步加速的想法?