[英]Speed up python's struct.unpack
我正在尝试加速我的脚本。 它基本上读取带有 Velodyne 的 Lidar HDL-32 信息的 pcap 文件,并允许我获取 X、Y、Z 和强度值。 我已经使用python -m cProfile ./spTestPcapToLas.py
分析了我的脚本,它在我的readDataPacket()
函数调用中花费了最多的时间。 在一个小型测试(80 MB 文件)中,解包部分花费了大约 56% 的执行时间。
我像这样调用readDataPacket
函数( chunk
是指 pcap 文件):
packets = []
for packet in chunk:
memoryView = memoryview(packet.raw())
udpDestinationPort = unpack('!h', memoryView[36:38].tobytes())[0]
if udpDestinationPort == 2368:
packets += readDataPacket(memoryView)
readDataPacket()
函数本身定义如下:
def readDataPacket(memoryView):
firingData = memoryView[42:]
firingDataStartingByte = 0
laserBlock = []
for i in xrange(firingBlocks):
rotational = unpack('<H', firingData[firingDataStartingByte+2:firingDataStartingByte+4])[0]
startingByte = firingDataStartingByte+4
laser = []
for j in xrange(lasers):
distanceInformation = unpack('<H', firingData[startingByte:(startingByte + 2)])[0] * 0.002
intensity = unpack('<B', firingData[(startingByte + 2)])[0]
laser.append([distanceInformation, intensity])
startingByte += 3
firingDataStartingByte += 100
laserBlock.append([rotational, laser])
return laserBlock
关于如何加快流程的任何想法? 顺便说一下,我使用 numpy 进行 X、Y、Z、强度计算。
Numpy 可以让你非常快速地做到这一点。 在这种情况下,我认为最简单的方法是直接使用ndarray
构造函数:
import numpy as np
def with_numpy(buffer):
# Construct ndarray with: shape, dtype, buffer, offset, strides.
rotational = np.ndarray((firingBlocks,), '<H', buffer, 42+2, (100,))
distance = np.ndarray((firingBlocks,lasers), '<H', buffer, 42+4, (100,3))
intensity = np.ndarray((firingBlocks,lasers), '<B', buffer, 42+6, (100,3))
return rotational, distance*0.002, intensity
这将返回单独的数组而不是嵌套列表,这应该更容易进一步处理。 作为输入,它需要一个buffer
对象(在 Python 2 中)或任何公开缓冲区接口的东西。 不幸的是,这取决于您的 Python 版本 (2/3) 您可以准确使用哪些对象。 但是这个方法非常快:
import numpy as np
firingBlocks = 10**4
lasers = 32
packet_raw = np.random.bytes(42 + firingBlocks*100)
%timeit readDataPacket(memoryview(packet_raw))
# 1 loop, best of 3: 807 ms per loop
%timeit with_numpy(packet_raw)
# 100 loops, best of 3: 10.8 ms per loop
提前编译Struct
,以避免使用模块级方法的 Python 级包装代码。 在循环外进行,因此不会重复支付建设成本。
unpack_ushort = struct.Struct('<H').unpack
unpack_ushort_byte = struct.Struct('<HB').unpack
Struct
方法本身在 CPython 中用 C 实现(并且模块级方法在解析格式字符串后最终委托给相同的工作),因此构建一次Struct
并存储绑定方法可以节省大量工作,特别是当解压少量值。
您还可以通过将多个值一起解包而不是一次解包来节省一些工作:
distanceInformation, intensity = unpack_ushort_byte(firingData[startingByte:startingByte + 3])
distanceInformation *= 0.002
正如Dan 所指出的,您可以使用iter_unpack
进一步改进这iter_unpack
,这将进一步减少字节代码执行和小切片操作的数量。
对于您的特定情况,如果您可以将循环放入 numpy 调用中,那将是最快的。
话虽如此,对于struct.unpack
部分 - 如果您的数据发生在本机字节顺序中,您可以使用memoryview.cast
。 举一个short
例子,它比struct.unpack
快约 3 倍,逻辑上没有任何变化。
In [20]: st = struct.Struct("<H")
In [21]: %timeit struct.unpack("<H", buf[20:22])
1.45 µs ± 26.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [22]: %timeit st.unpack(buf[20:22])
778 ns ± 10.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [23]: %timeit buf.cast("H")[0]
447 ns ± 4.16 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
您可以在一次调用中将原始distanceInformation
和intensity
值解包在一起。 特别是因为您只是将它们放在一个列表中:这就是unpack()
在解包多个值时所做的。 在您的情况下,您需要将distanceInformation
乘以0.002
,但您可以通过将其留到稍后来节省时间,因为您可以使用iter_unpack()
在一次调用中解析整个原始对列表。 该函数为您提供了一个生成器,可以使用itertools.islice()
对其进行切片,然后将其转换为列表。 像这样的东西:
laser_iter = struct.iter_unpack('<HB', firingData[firingDataStartingByte + 4])
laser = [[d * 0.002, i] for d, i in itertools.islice(laser_iter, lasers)]
不幸的是,这有点难以阅读,因此您可能想找到一种方法将其扩展到更多代码行中,使用更多描述性的变量名称,或者在您忘记为什么写这篇文章时为将来添加注释……
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.