Effizienter Weg, um konvertieren von string zu ctypes.c_ubyte array in Python
Ich habe einen string, 20 bytes, und ich möchte es zu konvertieren, eine ctypes.c_ubyte
array für Bitfeld-manipulation Zwecke.
import ctypes
str_bytes = '01234567890123456789'
byte_arr = bytearray(str_bytes)
raw_bytes = (ctypes.c_ubyte*20)(*(byte_arr))
Gibt es eine Möglichkeit zu vermeiden, eine Tiefe Kopie von str auf bytearray-zum Wohle der Besetzung?
Alternativ ist es möglich, zu konvertieren einen string in ein bytearray ohne eine Tiefe Kopie? (Mit Techniken wie memoryview?)
Ich verwende Python 2.7.
Performance-Ergebnisse:
Mit eryksun und Brian Larsen's Vorschlag, hier sind die benchmarks unter einer vbox-VM mit Ubuntu 12.04 und Python 2.7.
- methode1 nutzt mein original-Beitrag
- methode2 verwendet ctype from_buffer_copy
- Methode3 verwendet ctype cast/ZEIGER
- method4 verwendet numpy
Ergebnisse:
- methode1 nimmt 3.87 sec
- methode2 nimmt 0.42 sec
- Methode3 nimmt 1.44 sec
- method4 nimmt 8.79 Sek.
Code:
import ctypes
import time
import numpy
str_bytes = '01234567890123456789'
def method1():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
byte_arr = bytearray(str_bytes)
result = (ctypes.c_ubyte*20)(*(byte_arr))
t1 = time.clock()
print(t1-t0)
return result
def method2():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
result = (ctypes.c_ubyte * 20).from_buffer_copy(str_bytes)
t1 = time.clock()
print(t1-t0)
return result
def method3():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
result = ctypes.cast(str_bytes, ctypes.POINTER(ctypes.c_ubyte * 20))[0]
t1 = time.clock()
print(t1-t0)
return result
def method4():
result = ''
t0 = time.clock()
for x in xrange(0,1000000):
arr = numpy.asarray(str_bytes)
result = arr.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte*len(str_bytes)))
t1 = time.clock()
print(t1-t0)
return result
print(method1())
print(method2())
print(method3())
print(method4())
- Für 20 bytes, bezweifle ich, es gibt viel zu optimieren.
- Python-strings sind unveränderlich, so dass Sie nur zu tun haben mit den Kosten der Tiefe Kopie, wenn Sie Sie verändern wollen.
- Leider habe ich dazu paar tausend mal in der Sekunde. Dies ist immer ein hotspot für meinen code.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich nicht, dass die arbeiten, wie Sie denken.
bytearray
erstellt eine Kopie des Strings. Dann wird der interpreter entpackt diebytearray
- Sequenz in einstarargs
tuple
und fügt diese in ein neuestuple
die anderen Argumente (obwohl es gibt nichts in diesem Fall). Endlich, diec_ubyte
array-Initialisierer Schleifen über die argstuple
um die Elemente derc_ubyte
array. Das ist eine Menge Arbeit, und eine Menge zu kopieren, um durch zu gehen, nur um das array initialisiert.Stattdessen können Sie die
from_buffer_copy
Methode, vorausgesetzt, die Zeichenkette wird ein bytestring mit dem Puffer-Schnittstelle (nicht unicode):Dass immer noch kopieren Sie die Zeichenfolge, aber es ist nur einmal, und sehr viel effizienter. Wie gesagt in den Kommentaren, ein Python-string ist unveränderlich und könnte interniert oder als dict-key. Seine Unveränderlichkeit sollte respektiert werden, auch wenn ctypes können Sie gegen diesen in der Praxis:
Bearbeiten
Muss ich betonen, dass ich nicht empfehle mit ctypes zu ändern, eine unveränderliche CPython-string. Wenn Sie müssen, dann zumindest überprüfen
sys.getrefcount
zuvor, um sicherzustellen, dass die Referenz-Zählung von 2 oder weniger (Aufruf 1 addiert). Andernfalls werden Sie am Ende überrascht sein, durch die string-Praktikum für Namen (z.B."sys"
) und code-Objekt-Konstanten. Python ist frei, um wiederverwendet unveränderliche Objekte, wie es gerade passt. Wenn Sie Schritt außerhalb der Sprache zu mutieren einer 'unwandelbaren' - Objekt, Sie haben den Vertrag gebrochen.Zum Beispiel, wenn Sie ändern eine bereits gehashte Zeichenfolge, die zwischengespeicherten hash nicht mehr korrekt für den Inhalt. Die Pausen, die Sie zur Nutzung als dict-key. Weder ein anderer string mit dem neuen Inhalt, noch mit der original-Inhalt wird mit dem Schlüssel im dict. Die erstere hat einen anderen hash, und der letztere hat einen anderen Wert. Dann besteht der einzige Weg, um den dict-Element ist durch die Verwendung der mutierten Zeichenfolge hat die falsche hash. Fortsetzung aus dem vorigen Beispiel:
Betrachten wir nun die bescherung, wenn der Schlüssel ein interniert Zeichenfolge, die wiederverwendet in Dutzenden von stellen.
Zur performance-Analyse ist es üblich, verwenden Sie das timeit-Modul. Vor 3.3,
timeit.default_timer
je nach Plattform unterschiedlich. Auf POSIX-Systemen ist estime.time
, und unter Windows ist estime.clock
.Als eine andere Lösung für Sie zu benchmark - (ich wäre sehr interessiert an den Ergebnissen).
Mithilfe von numpy könnte hinzufügen, eine gewisse Einfachheit abhängig davon, was der ganze code aussieht.