加速python的struct.unpack

Xav*_*ino 7 python performance numpy unpack lidar

我正在努力加快我的脚本.它基本上用Velodyne的Lidar HDL-32信息读取pcap文件,并允许我获得X,Y,Z和Intensity值.我已经使用了我的脚本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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

关于如何加快这个过程的任何想法?顺便说一句,我正在使用numpy进行X,Y,Z,Intensity计算.

Sha*_*ger 8

Struct提前编译,以避免使用模块级方法的Python级别包装代码.在环路外进行,因此不会反复支付建设成本.

unpack_ushort = struct.Struct('<H').unpack
unpack_ushort_byte = struct.Struct('<HB').unpack
Run Code Online (Sandbox Code Playgroud)

Struct本身C实现在CPython的(和模块级方法最终委托给相同的工作解析格式字符串之后),所以建立方法Struct一次并存储绑定方法节省了工作的一个非平凡量,拆包特别是当少量的价值观.

您还可以通过将多个值一起解压缩来保存一些工作,而不是一次解压缩一个:

distanceInformation, intensity = unpack_ushort_byte(firingData[startingByte:startingByte + 3])
distanceInformation *= 0.002
Run Code Online (Sandbox Code Playgroud)

正如Dan所说,你可以进一步改进这一点iter_unpack,这将进一步减少字节码执行量和小切片操作.


小智 7

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
Run Code Online (Sandbox Code Playgroud)

这将返回单独的数组而不是嵌套列表,这应该更容易进一步处理.作为输入,它需要一个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
Run Code Online (Sandbox Code Playgroud)