2017-10-11 82 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一样快。还是有人有进一步加速的想法?