Kontakt
DSVGO
Historie | |
06.09.2001 | Beschleunigung durch Optimierung der API-Deklaration |
04.09.2000 | Erste Version |
Soll der Inhalt zweier String-Variablen getauscht werden, so denkt man zuerst an die übliche Kopieraktion via Hilfsvariable in drei Schritten, z.B.:
'a und b tauschen: x = a a = b b = x
Als wiederverwendbare Prozedur könnte man z.B. folgendes schreiben: (Die Parameterübergabe erfolgt via ByRef, s.a. "ByVal oder ByRef - Parameter richtig deklarieren, Tipps zur Verwendung".)
Public Sub SwapStr(ByRef a As String, ByRef b As String) Dim Tmp As String Tmp = a a = b b = Tmp End Sub
Dies funktioniert auch wie gewünscht, allerdings sehr langsam. So werden bei Strings der Länge 1.000 insgesamt 6.000 Bytes verschoben: Interne Darstellung eines Zeichens kostet 2 Bytes x 1.000 Zeichen x 3 Kopiervorgänge.
Wäre es nicht schöner, wenn nur die internen Adressen der String-Variablen vertauscht werden würden? Dann müßten (unabhängig von der String-Länge) nur 12 Bytes verschoben werden: Interne Darstellung einer Adresse kostet 4 Bytes x 3 Kopiervorgänge.
Dies geht tatsächlich, unter Verwendung der API-Routine RtlMoveMemory; Diese Routine muss im Deklarationsteil bekannt gemacht werden:
Private Declare Sub RtlMoveMemory Lib "kernel32" ( _ ByVal DestPtr As Long, ByVal SourcePtr As Long, _ ByVal Bytes As Long)
Der Austausch ist dann (fast) trivial. Man beachte, dass aus Performance-Gründen StrPtr statt RtlMoveMemory (s. auskommentierten Code) für die erste Kopieraktion verwendet wird:
Public Sub SwapStr(ByRef a As String, ByRef b As String) Dim p As Long 'Hilfs-Pointer 'RtlMoveMemory VarPtr(p), VarPtr(a), Len(p) Ptr = StrPtr(a) RtlMoveMemory VarPtr(a), VarPtr(b), Len(p) RtlMoveMemory VarPtr(b), VarPtr(p), Len(p) End Sub
Diese Lösung ist immer schneller als die oben gezeigte (überraschenderweise selbst bei Strings der Länge 1 schon 3x): Bei 10 Zeichen etwa 4x, bei 1.000 Zeichen etwa 25x, bei 100.000 Zeichen etwa 2750x, etc. pp. (kompiliert und optimiert).
Durch Verwendung der folgenden Alias-Deklaration kann noch ein wenig mehr Performance herausgekitzelt werden. Value wird als ByRef deklariert, so dass VarPtr für die Angabe der Kopierquelle entfallen kann. Außerdem wird die Anzahl der zu kopierenden Bytes auf 4 voreingestellt, so dass der API-Aufruf sehr viel einfacher wird:
Private Declare Sub PokeLng Lib "kernel32" Alias "RtlMoveMemory" ( _ ByVal Addr As Long, ByRef Value As Long, _ Optional ByVal Bytes As Long = 4)
Damit sieht die Swap-Routine dann wirklich elegant aus:
Public Sub SwapStr(ByRef a As String, ByRef b As String) Dim p As Long 'Hilfs-Pointer p = StrPtr(a) PokeLng VarPtr(a), StrPtr(b) PokeLng VarPtr(b), p End Sub
© Jost Schwider, 04.09.2000-06.09.2001 - http://vb-tec.de/swapstr.htm