hello out there...
i know there is the Win32 API command lstrlen() to get the length of a string. As we all know, Win32 API functions are not ever the fastest and so i did some research and come up with following speed optimized lstrlen() replacement (based on Win32 API only), which seems to be a lot faster as the original version.
Text.s = "What do you think about this?"
lResult = HeapSize_( GetProcessHeap_(), 0, @Text ) - 5
MessageBox_( 0, Str( lResult ), "StringLen", 0 )
It seems this replacement is a nice way to speed things up, but is this way really a clean way or more like a dirty hack? What do you think about this code? Any risk to use this in prods?
PS: Seems to work with PB...
Va!n thats a great little bit of code! Karma++ for the post. It looks legitimate to me, judging by MSDN docs and dates. Only uses kernel32.dll so can even be used in tiny code productions.
the fastest way to get the length of a zero terminated string in DOS, though its not win32 it can still be used in win32 as a macro or proceedure bypassing the api altogether (by the time those push and pops are done this code has already begin scanning the string for zero terminator):
lea edi,address ; address of string
mov ecx,-1 ; maximum string length = 0xFFFFFFFF
xor eax,eax ; search for zero terminator
repnz scasb ; find first zero terminator byte
not ecx ; ecx was counting down so ecx inverted is string length
and the same thing for normal strings
lea edi,address ; address of string
mov ecx,-1 ; maximum string length = 0xFFFFFFFF
mov ax,"$" ; search for string terminator
repne scasb ; find first string terminator byte
not ecx ; ecx was counting down so ecx inverted is string length
thanks taj :)
Speed comparsion between PB internal functions, Win32 API lstrlen() and my version:
; -------- Program by "Mr.Vain/Secretly!" aka Thorsten Will in 2008 --------
DisableDebugger
Text.s = InputRequester("Fast Len() routine by Mr.Vain/Secretly!", "Type a string of any size", "What do you think about this?")
start1 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult1 = Len( Text.s )
Next
start1 = ElapsedMilliseconds() - start1
start2 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult2 = MemoryStringLength( @Text ) * SizeOf( Character )
Next
start2 = ElapsedMilliseconds() - start2
start3 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult3 = lstrlen_( @Text )
Next
start3 = ElapsedMilliseconds() - start3
start4 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult4 = HeapSize_( GetProcessHeap_(), 0, @Text ) - 5
Next
start4 = ElapsedMilliseconds() - start4
Msg1.s = "PB Len():" + #TAB$ + #TAB$ + #TAB$ + Str( lResult1 ) + " len" +#LFCR$
Msg1.s = Msg1.s + "PB MemoryStringLenght():" + #TAB$ + Str( lResult2 ) + " len"+ #LFCR$
Msg1.s = Msg1.s + "Win32 API - lstrlen():" + #TAB$ + Str( lResult3 ) + " len"+ #LFCR$
Msg1.s = Msg1.s + "My Version:" + #TAB$ + #TAB$ + Str( lResult4 ) + " len"+ #LFCR$
Msg2.s = "PB Len():" + #TAB$ + #TAB$ + #TAB$ + Str( start1 ) + " ms" +#LFCR$
Msg2.s = Msg2.s + "PB MemoryStringLenght():" + #TAB$ + Str( start2 ) + " ms"+ #LFCR$
Msg2.s = Msg2.s + "Win32 API - lstrlen():" + #TAB$ + Str( start3 ) + " ms"+ #LFCR$
Msg2.s = Msg2.s + "My Version:" + #TAB$ + #TAB$ + Str( start4 ) + " ms"+ #LFCR$
MessageRequester( "Results:", Msg1.s, 0 )
MessageRequester( "Speed:" , Msg2.s, 0 )
Executeable can be downloaded to see the speed improvement =) But i am still not really sure if this way works in and for all situations and if its a dirty hack (cheating) or correct coding.
[Edited]
Zip includes ascii and unicode test versions!
Here is a version, works with Unicode and Non-Unicode strings :)
; -------- Program by "Mr.Vain/Secretly!" aka Thorsten Will in 2008 --------
DisableDebugger
Text.s = InputRequester("Fast Len() routine by Mr.Vain/Secretly!", "Type a string of any size", "What do you think about this?")
start1 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult1 = Len( Text.s )
Next
start1 = ElapsedMilliseconds() - start1
start2 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult2 = MemoryStringLength( @Text ) * SizeOf( Character )
Next
start2 = ElapsedMilliseconds() - start2
start3 = ElapsedMilliseconds()
For i = 0 To 20000000
lResult3 = lstrlen_( @Text )
Next
start3 = ElapsedMilliseconds() - start3
start4 = ElapsedMilliseconds()
CompilerIf #PB_Compiler_Unicode
lOffset = 10
CompilerElse
lOffset = 5
CompilerEndIf
lCharType = SizeOf(character) >> 1
For i = 0 To 20000000
lResult4 = (HeapSize_( GetProcessHeap_(), 0, @Text ) - lOffSet ) >> lCharType
Next
start4 = ElapsedMilliseconds() - start4
Msg1.s = "PB Len():" + #TAB$ + #TAB$ + #TAB$ + Str( lResult1 ) + " len" +#LFCR$
Msg1.s = Msg1.s + "PB MemoryStringLenght():" + #TAB$ + Str( lResult2 ) + " len"+ #LFCR$
Msg1.s = Msg1.s + "Win32 API - lstrlen():" + #TAB$ + Str( lResult3 ) + " len"+ #LFCR$
Msg1.s = Msg1.s + "My Version:" + #TAB$ + #TAB$ + Str( lResult4 ) + " len"+ #LFCR$
Msg2.s = "PB Len():" + #TAB$ + #TAB$ + #TAB$ + Str( start1 ) + " ms" +#LFCR$
Msg2.s = Msg2.s + "PB MemoryStringLenght():" + #TAB$ + Str( start2 ) + " ms"+ #LFCR$
Msg2.s = Msg2.s + "Win32 API - lstrlen():" + #TAB$ + Str( start3 ) + " ms"+ #LFCR$
Msg2.s = Msg2.s + "My Version:" + #TAB$ + #TAB$ + Str( start4 ) + " ms"+ #LFCR$
MessageRequester( "Results:", Msg1.s, 0 )
MessageRequester( "Speed:" , Msg2.s, 0 )
Yours works fine here (XP, SP2) and its much much faster.
Taj
Thats not bad about 1/4 the time to do the same work as the other routines not bad at all
Has someone tested it on Vista business? i got feedback on another forum:
Quote
Unfortunately it doesn't seem to work on my 4.2b2 Vista business setup.
I'm getting
pb len 29
pb memorystring 29
win32 29
my version 86173 Shocked
But it is fast!
and another member has posted a small example and saying its not good to use this technique, but i think its due fact of how he is using it. He is allocating a big memory block and just copy only the string (afaik without string end-terminater sign) to the big allocated memoryblock... i think i would never do such a thing in my actual project nor in future projects. So the questions are now: Does this methode works on Vista too and is this routine safe when just only giving the adress of a string instead following example by a member from another forum:
Quote
You shouldn't use this, because you check the size of the memory block that the string contains. This must not be the size of the string itself.
Text.s = "Hallo Test"
*Memory = AllocateMemory(10000000)
CopyMemory(@Text,*Memory,Len(Text)+SizeOf(Character))
CompilerIf #PB_Compiler_Unicode
Offset = 10
CompilerElse
Offset = 5
CompilerEndIf
CharType = SizeOf(Character) >> 1
Result4 = (HeapSize_( GetProcessHeap_(), 0, *Memory ) - Offset ) >> CharType
[Info]Added unicode executeable version to the zip archive!
Quote from: va!n on February 29, 2008
He is allocating a big memory block and just copy only the string (afaik without string end-terminater sign) to the big allocated memoryblock...
And this years Darwin award for C programming goes to...
How do you know your string is going to be allocated on the heap? What if it's a static string. And how do you know that Windows or PB hasn't padded the allocation to the next multiple of 4 (or more bytes)?
The real question is why you need a fast string length anyway. One thing you can do is use a Pascal style string where all the string manipulation functions keep a track of how long the string is.
eg. in C
struct
{ int len; char *string;} mystring;
I've been to a seminar where Microsoft claimed that win32 API memcpy,memcmp, strcpy etc would always be the *guaranteed* fastest way to do these things on your OS/CPU combination. I've never put that to the test myself.
Jim
I guess what Microsoft mean, correct me if I am wrong, is that for any possible use of a string, these routines are completely safe and optimal. Which is their job. I'm not sure that should also imply that in specific use cases they are non-optimal but it does seem to. So I guess the conclusion is Va!ns routine is not safe to put into a string dll. But it works fine for your own code.
I'm still wondering how long your strings are that you're actually in need of faster strlen-function ???
Ok, I remember once exporting 180mb of xml from an obscure software that refused to add newlines...
But have you really profiled your mainloop and found its spending most of the time doing strlen?
If yes: your code sucks ;)
To answer some questions i have to point out following.... i dont really need a faster Len() command but i am someone who like to optimize where its possible. Why should i use slower things when there are ways to speed things a lot up like Len() ?
But the mainreason is i am coding with PB and i dont want to include the PB lib, because most libs are in my eyes to big and to buggy. If you may ask why dont code in C/CPP... the answer is simply i dont know how and where to start (in other words i have problems to work with VCs IDE), else i would still try to code in CPP (VisualC). But i will try to learn coding with VC in the future to be independent from PB and having more flexibility and even speed ^^
Try using your function to do Len("banana")
Jim