2016-04-22 10 views
3

Я пытаюсь ускорить мой скрипт. Он в основном читает файл pcap с информацией Lidar HDL-32 от Velodyne и позволяет мне получать значения X, Y, Z и Intensity. Я профилировал свой сценарий, используя python -m cProfile ./spTestPcapToLas.py, и он тратит больше всего времени на мои вызовы функций readDataPacket(). В небольшом тесте (файл 80 МБ) часть распаковки занимает около 56% времени выполнения.Ускорить работу python struct.unpack

я называю 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, Intensity.

ответ

3

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 
+0

Это привело к увеличению скорости на 30 раз для этой конкретной функции. Огромное спасибо. : D –

0

Вы можете распаковать исходные значения 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)] 

К сожалению, это немного сложнее читать, так что вы можете захотеть, чтобы найти способ, чтобы распространить на это в несколько строк кода, с более описательные имена переменных, или добавить комментарий к будущее, когда вы забудете, почему вы написали это ...

+0

К сожалению, я не могу использовать Python 3. Я использую Python 2.7.11. Вы знаете другое решение? –

3

Скопируйте Struct раньше времени, чтобы избежать кода обертывания уровня Python с помощью методов уровня модуля. Сделайте это за пределами петель, поэтому стоимость строительства не выплачивается повторно.

unpack_ushort = struct.Struct('<H').unpack 
unpack_ushort_byte = struct.Struct('<HB').unpack 

Struct В Сами методы реализуются в C в CPython (и методы уровня модуля, в конечном счете, делегирующие на ту же работу после разбора строки формата), поэтому построение Struct раз и хранения связанных методов экономит не- тривиальный объем работы, особенно при распаковке небольшого количества значений.

Вы также можете сохранить некоторую работу по распаковке несколько значений вместе, а не один на один раз:

distanceInformation, intensity = unpack_ushort_byte(firingData[startingByte:startingByte + 3]) 
distanceInformation *= 0.002 

Как Dan notes, вы могли бы еще больше улучшить это с iter_unpack, что еще больше уменьшить количество байт-код выполнение и операции малого сегмента.

+0

Я бы предложил проверить мой метод 'iter_unpack', прежде чем быть уверенным, что он улучшает производительность - я думаю, он создает много временных объектов. Ваш метод звучит более уверенно. –

 Смежные вопросы

  • Нет связанных вопросов^_^