|
-
Apr 18th, 2007, 05:46 AM
#1
Faster InStrRev
As many of us know the InStrRev in VB6 is on the slower side. The bigger strings you give it the slower it gets, leaving room for a lot of improvement.
I made this replacement function that gives some improvement against the original function, although I didn't optimize the maximum out of it to keep it simple for anyone to use.
Code:
Option Explicit
Private Declare Sub RtlMoveMemory Lib "ntdll.dll" (ByRef lpvDest As Any, ByRef lpvSrc As Any, ByVal cbLen As Long)
'Private Declare Function VarPtrArray Lib "msvbvm50.dll" Alias "VarPtr" (Var() As Any) As Long
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() As Any) As Long
Public Function InStrRev(ByRef String1 As String, ByRef String2 As String, Optional ByVal Start As Long = -1, Optional ByVal CompareMethod As VbCompareMethod = vbBinaryCompare) As Long
Static BufStrHeader(5) As Long, BufFindHeader(5) As Long, BufFind2Header(5) As Long
Dim BufStr() As Integer, BufFind() As Integer, BufFind2() As Integer, KeyLC As String, KeyUC As String
Dim intComp1 As Integer, intComp2 As Integer, intComp3 As Integer, intComp4 As Integer
Dim intComp1b As Integer, intComp3b As Integer
Dim lngA As Long, lngB As Long, lngStartEnd As Long, lngKeyEnd As Long
lngKeyEnd = Len(String2)
If LenB(String1) = 0 Or lngKeyEnd = 0 Then Exit Function
lngStartEnd = Len(String1) - lngKeyEnd
If Start < 1 Then
Start = lngStartEnd
ElseIf Start > lngStartEnd Then
Start = lngStartEnd
Else
Start = Start - 1
End If
BufStrHeader(0) = 1
BufStrHeader(1) = 2
BufStrHeader(3) = StrPtr(String1)
BufStrHeader(4) = &H7FFFFFFF
RtlMoveMemory ByVal VarPtrArray(BufStr), VarPtr(BufStrHeader(0)), 4
If CompareMethod = vbBinaryCompare Then
BufFindHeader(0) = 1
BufFindHeader(1) = 2
BufFindHeader(3) = StrPtr(String2)
BufFindHeader(4) = &H7FFFFFFF
RtlMoveMemory ByVal VarPtrArray(BufFind), VarPtr(BufFindHeader(0)), 4
If lngKeyEnd = 1 Then
intComp1 = BufFind(0)
For lngA = Start To 0 Step -1
intComp2 = BufStr(lngA)
If intComp1 = intComp2 Then Exit For
Next lngA
If lngA > -1 Then InStrRev = lngA + 1
Else
lngKeyEnd = lngKeyEnd - 1
intComp1 = BufFind(lngKeyEnd)
For lngA = Start + lngKeyEnd To lngKeyEnd Step -1
intComp2 = BufStr(lngA)
If intComp1 = intComp2 Then
For lngB = 0 To lngKeyEnd - 1
intComp3 = BufFind(lngB)
intComp4 = BufStr(lngA - lngKeyEnd + lngB)
If intComp3 <> intComp4 Then Exit For
Next lngB
If lngB = lngKeyEnd Then InStrRev = lngA - lngKeyEnd + 1: Exit For
End If
Next lngA
End If
RtlMoveMemory ByVal VarPtrArray(BufStr), 0&, 4
RtlMoveMemory ByVal VarPtrArray(BufFind), 0&, 4
Else
KeyUC = UCase$(String2)
KeyLC = LCase$(String2)
BufFindHeader(0) = 1
BufFindHeader(1) = 2
BufFindHeader(3) = StrPtr(KeyUC)
BufFindHeader(4) = &H7FFFFFFF
RtlMoveMemory ByVal VarPtrArray(BufFind), VarPtr(BufFindHeader(0)), 4
BufFind2Header(0) = 1
BufFind2Header(1) = 2
BufFind2Header(3) = StrPtr(KeyLC)
BufFind2Header(4) = &H7FFFFFFF
RtlMoveMemory ByVal VarPtrArray(BufFind2), VarPtr(BufFind2Header(0)), 4
If lngKeyEnd = 1 Then
intComp1 = BufFind(0)
intComp1b = BufFind2(0)
For lngA = Start To 0 Step -1
intComp2 = BufStr(lngA)
If intComp1 = intComp2 Or intComp1b = intComp2 Then Exit For
Next lngA
If lngA > -1 Then InStrRev = lngA + 1
Else
lngKeyEnd = lngKeyEnd - 1
intComp1 = BufFind(lngKeyEnd)
intComp1b = BufFind2(lngKeyEnd)
For lngA = Start + lngKeyEnd To lngKeyEnd Step -1
intComp2 = BufStr(lngA)
If intComp1 = intComp2 Or intComp1b = intComp2 Then
For lngB = 0 To lngKeyEnd - 1
intComp3 = BufFind(lngB)
intComp3b = BufFind2(lngB)
intComp4 = BufStr(lngA - lngKeyEnd + lngB)
If intComp3 <> intComp4 And intComp3b <> intComp4 Then Exit For
Next lngB
If lngB = lngKeyEnd Then InStrRev = lngA - lngKeyEnd + 1: Exit For
End If
Next lngA
End If
RtlMoveMemory ByVal VarPtrArray(BufStr), 0&, 4
RtlMoveMemory ByVal VarPtrArray(BufFind), 0&, 4
RtlMoveMemory ByVal VarPtrArray(BufFind2), 0&, 4
End If
End Function
The major optimizations I can think of:- Use Boyer-Moore or other text searching algorithm for keywords that are longer than 5 characters (binary compare) or 3 characters (text compare).
- Pass string pointers and string lengths instead of passing the string datatype (in which case I'd suggest renaming the function to InPtrRevW for UTF-16 version and InPtrRevA for ANSI version).
- Only call RtlMoveMemory when required and when program ends (instead of always in the beginning of each function call). This could mean wrapping the function into a Class Module to avoid extra function calls to initialize and clean up; although classes are far slower.
- It is possible to declare API functions in a faster way in VB6 by making a custom function and faking it to point to the API function. Thus we could get faster access to RtlMoveMemory and VarPtrArray.
You don't need to use this code as a base to your own work, although you might find some tricks in it that are useful (especially the safearray structure).
Note!
It is important when working with speed orientated things that you benchmark only compiled code. Another big thing that affects speed are the advanced optimizations in the compile tab when you're setting the options of your executable. Ticking everything on can have a major effects on the compiled speed of your program if you've used a lot of math and arrays. However, this also makes your program more unstable if you have any errors in your code as there will be no array boundary checks for example.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|