2016-04-22 9 views
3

Ich versuche, mein Skript zu beschleunigen. Es liest grundsätzlich eine pcap-Datei mit Velodyne Lidar HDL-32 Informationen und ermöglicht es mir, X-, Y-, Z- und Intensitätswerte zu erhalten. Ich habe mein Skript mit python -m cProfile ./spTestPcapToLas.py profiliert und es kostet die meiste Zeit in meinem readDataPacket() Funktionsaufrufe. In einem kleinen Test (80 MB-Datei) dauert der Entpackungsabschnitt etwa 56% der Ausführungszeit.Beschleunigung Python struct.unpack

Ich nenne die readDataPacket Funktion wie folgt (chunk auf die pcap-Datei verweist):

packets = [] 
for packet in chunk: 
    memoryView = memoryview(packet.raw()) 
    udpDestinationPort = unpack('!h', memoryView[36:38].tobytes())[0] 

    if udpDestinationPort == 2368: 
     packets += readDataPacket(memoryView) 

Die readDataPacket() Funktion selbst wie folgt definiert ist:

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 

Alle Ideen, wie ich beschleunigen kann den Prozess? Übrigens verwende ich numpy für die X, Y, Z, Intensitätsberechnungen.

Antwort

3

Mit Numpy können Sie das sehr schnell machen. In diesem Fall denke ich, die einfachste Art und Weise direkt auf den ndarray Konstruktor zu verwenden ist:

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 

Diese separate Arrays anstelle der verschachtelten Liste zurück, die weiter viel leichter zu verarbeiten sein sollte. Als Eingabe benötigt es ein buffer Objekt (in Python 2) oder irgendetwas, das die Pufferschnittstelle freilegt. Leider hängt es von deiner Python-Version (2/3) ab, welche Objekte du genau verwenden kannst.Aber diese Methode ist sehr schnell:

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

Dies führte zu ~ 30-facher Geschwindigkeitssteigerung für diese spezielle Funktion. Ich danke dir sehr. : D –

0

Sie können die rohen distanceInformation und intensity Werte in einem Aufruf zusammen auspacken. Vor allem, weil Sie sie nur zusammen in eine Liste stellen: Das macht unpack(), wenn es mehrere Werte entpackt. In Ihrem Fall müssen Sie dann die distanceInformation durch 0.002 multiplizieren, aber Sie können Zeit sparen, indem Sie dies bis später, weil Sie iter_unpack() verwenden können, um die gesamte Liste der rohen Paare in einem Aufruf zu analysieren. Diese Funktion gibt Ihnen einen Generator, der mit itertools.islice() geschnitten und dann in eine Liste umgewandelt werden kann. Etwas wie folgt aus:

laser_iter = struct.iter_unpack('<HB', firingData[firingDataStartingByte + 4]) 
laser = [[d * 0.002, i] for d, i in itertools.islice(laser_iter, lasers)] 

Leider ist dies etwas schwieriger zu lesen, so dass Sie könnte dies finden wollen, einen Weg, um sich auszubreiten in mehr Zeilen Code, mit mehr beschreibende Variablennamen oder einen Kommentar für das hinzufügen Zukunft, wenn Sie vergessen, warum Sie dies geschrieben haben ...

+0

Leider kann ich Python 3 nicht verwenden. Ich benutze Python 2.7.11. Kennen Sie eine andere Lösung? –

3

Übersetzen Sie eine Struct vor der Zeit, um die Python-Ebene Umbruch-Code mit den Modulebenen Methoden zu vermeiden. Tun Sie es außerhalb der Schleifen, so dass die Baukosten nicht wiederholt bezahlt werden.

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

Die Struct Methoden selbst in C in CPython implementiert werden (und Modulebene Methoden schließlich dasselbe Arbeits delegieren nach der Formatzeichenfolgenanalyse), so dass die einmal Struct Aufbau und gebundene Methoden Speicher speichern eine nicht trivialer Arbeitsaufwand, insbesondere beim Auspacken einer kleinen Anzahl von Werten.

Sie auch zusammen einige Arbeit durch Auspacken mehrere Werte speichern können, anstatt einer nach dem anderen:

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

Als Dan notes, könnten Sie dies weiter verbessern mit iter_unpack, was die Menge an Bytecode weiter reduzieren würde Ausführung und kleine Slice-Operationen.

+0

Ich schlage vor, meine 'iter_unpack'-Methode zu testen, bevor ich zu sicher bin, dass sie die Leistung verbessert - es schafft viele temporäre Objekte, denke ich. Ihre Methode klingt sicherer. –

Verwandte Themen