我有一个巨大的二进制文件(几GB),它有以下数据格式:
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')
此代码有效,但对于我的目的来说它太慢了(对于100k数据点,每个4字节为2s)。至少我需要10倍的速度因子。
分析器说代码花费的时间主要是字符串格式化(获取位)和_unpack_integer()。
不幸的是我不知道如何继续这里。我正在考虑使用cython或直接编写一些c代码来进行读取。我也尝试过Pypy ant,它给了我巨大的性能提升但不幸的是它需要与更大的项目兼容与Pypy合作。
答案 0 :(得分:1)
如果您已经有一个识别数据结构的c / c ++库,我建议您尝试ctypes。好处是,数据结构仍然可用于你的python,而'loading'会很快。如果您已经有一个c库来加载数据,您可以使用该库中的函数调用来完成繁重的工作,并将数据映射到您的python结构中。对不起,我将无法尝试为您的示例提供正确的代码(可能是其他人的手杖),但这里有一些提示可以帮助您入门
我对如何在python中创建位向量的看法: https://stackoverflow.com/a/40364970/262108
我上面提到的方法,我应用于你描述的类似问题。在这里,我使用ctypes创建一个ctypes数据结构(从而使我能够将该对象用作任何其他python对象),同时还能够将它传递给C库:
答案 1 :(得分:1)
由于Jean-FrançoisFabre的暗示,我发现了一个使用位掩码的合适的方法,与问题中的代码相比,这使得我的速度提高了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一样快。或者有人有进一步加速的想法吗?