AutoHotkey Community

It is currently May 26th, 2012, 1:12 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 93 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7  Next
Author Message
 Post subject:
PostPosted: November 4th, 2008, 9:53 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
With only the GetProcAddress optimization, a call to MulDiv takes 1.1µs on my system.

Changing the parameter types to str from int reduces the time by 0.2µs. Now no conversion is done - the function receives the address of the numeric literal, which is stored as a string. Up to this point, approximately 0.5µs (nearly 50%!) passes outside of DllCall.

If variable references or string literals are passed in place of numeric literals, the time spent outside DllCall is reduced by 0.1µs, with no visible affect on DllCall itself. Apparently numeric literals require more processing than variable references or string literals during expression evaluation.

It is not quite as clear-cut as "DllCall is slow." The efficiency of your code and the methods you use are more important. The first post in this topic contains a prime example: SetPixel is slow, regardless of which language you call it from.

Ordinarily, the solution would be to retrieve all of the image data at once, perform processing on it, then give the bitmap the updated image data. SKAN's example takes approximately 200ms on my system, which is considerably faster than Pil's original example. However, if I replace
Code:
X:=0,B:=0, , OffSet := &BMP+54
Loop 256 {                ; Altering RGB values   
 X:=X+1, Y:=0
 Loop 256
  Offset := NumPut( ((b<<16)|(y++<<8)|x), Offset+0 ) - 1
}
with the equivalent machine code (compiled C++):
Code:
;LowLevel.ahk required.
prepare_bitmap(bmp){
}LowLevel_init(),__mcode("prepare_bitmap"
, "8B4424088B088B118B0283C03633D29033C988104088084"
. "0C60000414081F9000100007CED4281FA000100007CE2C3")
prepare_bitmap(BMP)
it takes 0, 16 or 30ms. It's too fast for A_TickCount to get an accurate reading! AutoHotkey is simply not suitable for this kind of processing.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 4th, 2008, 1:52 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
It's been on my to-do list for a long time to change the script loading process to pre-tokenize and pre- infix-to-postfix all expressions (currently, only an expression's variables and functions are looked up in advance). In addition to catching more types of errors before the script begins running, this would also improve performance because those steps would be done only once (when the script starts) rather than each time the expression is encountered (such as in a loop). This would speed up all expressions and function calls (including built-in functions like DllCall).

The disadvantage is that it would use more memory because both the postfix and infix versions of each expression would be stored (for use by ListLines and error dialogs); it would also slow down launch time. But on balance, it seems worth it.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 4th, 2008, 2:48 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
I've also added the following to the DllCall page in the documentation:

In v1.0.46.08+, even faster performance can be achieved by looking up the function's address beforehand. For example:
Code:
MulDivProc := DllCall("GetProcAddress", uint, DllCall("GetModuleHandle", str, "kernel32"), str, "MulDiv")  ; But if the DLL isn't yet loaded, first use LoadLibrary.
Loop 500
    DllCall(MulDivProc, int, 3, int, 4, int, 3)

Thanks for the idea.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 5th, 2008, 1:10 am 
Offline

Joined: February 26th, 2006, 9:45 am
Posts: 55
Location: Recife Brazil
Really impressive.. On my machine SKAN´s code speeded up (using machine code in loop) from 320 to 31 msec.
Another nice example of what can be done with LowLevel.ahk

However there is no DllCall in the loop, that´s why SKAN placed it off topic.
Ofcourse efficiency of code is very important but my main use of DllCall is obtaining data on port with WinIO.dll (license CPL). Source c++ you can find at http://www.autohotkey.net/~pil/WinIo.rar I can´t find a work-around; only solution I found was writing a buffer(dll) in another language.
I tried Lazzlo´s sugestion translating the dll in machine code with the help of disasm.exe but had a lot of errors. I remember that Lazzlo himself had to deviate because of non-portable data reference when he tried to disassemble an existing dll.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 5th, 2008, 6:19 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Pil wrote:
However there is no DllCall in the loop
That is exactly why it is a good example - if the script is too slow, there's a good chance DllCall overhead is not the issue.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 6th, 2008, 1:42 am 
Offline

Joined: February 26th, 2006, 9:45 am
Posts: 55
Location: Recife Brazil
OK I understand your point of view.
AHK´s help file says "DllCall returns the actual value returned by the function". Maybe the variable PortValIn can be treated different soon after call. Code below waits until a certain pattern appears on the parallel port (or loop finish) The io.dll (45kb) you can find at http://www.autohotkey.net/~pil/io.dll Time needed on my computer = 657 msec.
Code:
#NoEnv
Process, Priority,,R
hModule := DllCall("LoadLibrary", "str", "io.dll")
PortOut :=DllCall("GetProcAddress",UInt,DllCall("GetModuleHandle",Str,"io"),Str,"PortOut")
PortIn := DllCall("GetProcAddress",UInt,DllCall("GetModuleHandle",Str,"io"),Str,"PortIn")

;First let us see if port will be read.
;You don´t need hardware, just send a value to the parallel port
;and then read the data on that same port.

PortValOut := 100
PortValIn := 0
DllCall(PortOut,int,888,char,PortValOut)   
PortValIn := 0xFF & DllCall(PortIn,char,888)

If PortvalIn = %PortValOut%
MsgBox, Port has been read!
Else MsgBox Oops... Not working, maybe io.dll is not in script directory! 

StartTime := A_TickCount
Loop 65025 {
         PortValIn := 0xFF & DllCall(PortIn,char,888)
         If PortValIn = 255     ; ? Maybe something can be changed here
         break
         }
NeededTime := A_TickCount - StartTime
MsgBox, Time needed was %NeededTime% milliseconds


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 6th, 2008, 9:58 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Pil wrote:
AHK´s help file says "DllCall returns the actual value returned by the function". Maybe the variable PortValIn can be treated different soon after call.
I don't see your point, or the relevance to this thread's topic...?

Tip: You don't need to use GetModuleHandle("io") as hModule already contains the value you need.

Tip: If you use the UChar return type, you won't need 0xFF &.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 7th, 2008, 12:27 am 
Offline

Joined: February 26th, 2006, 9:45 am
Posts: 55
Location: Recife Brazil
First of all thank you for the tips
Any way I only need "0xFF &" on older computers I think when the logical address of the computer CPU is 32 bits. There seems to be some garbage left on the varable which gives a wrong value.

You wrote:
It is not quite as clear-cut as "DllCall is slow." The efficiency of your code and the methods you use are more important.

So I am triing to improve my code/methods.
I need to monitor the parallel port for (very short) pulses so I have to put a DllCall in loop.
A Slow call will simply overlook short pulses, because/while processor is calculating. Of course the comparison "If PortValIn = 255" in loop will do the same, And it´s the only thing left to be improved.
Ahk`s help file says DllCall returns the actual value returned by the function.
So my question is; result is not necessariament an 64 byte long AHK variable? If no is there maybe som low level technique for comparision without the need of time taking conversion ?
Can you imagine some improvement?
The PortIn function is like the SetPixel function tem times slower then in other languages I tried.
Correction...after your GetProcAddress solution just 6,7 times slower!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 7th, 2008, 10:54 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Pil wrote:
Any way I only need "0xFF &" on older computers I think when the logical address of the computer CPU is 32 bits.
"0xFF &" truncates to a value between 0 and 255 inclusive - i.e. an 8-bit value. Return values of 32-bit or less are passed via the eax register. If the function is defined as returning an unsigned 8-bit integer, the upper 24 bits of eax are undefined, and can have any value. Thus, if you leave the return type at the default "int" (32-bit signed integer), of course you'll get junk.

I would not recommend relying on the "junk" to be 0 on certain processors, as it is presumably undocumented and subject to change.
Quote:
So my question is; result is not necessariament an 64 byte long AHK variable?
What makes you think AHK variable's are 64 bytes long? Even if 64 bytes are allocated for the variable, setting an integer between 0 and 255 will write only 2-5 bytes - "0" is 2 bytes including the null-terminator, while "0xff" is 5. More to the point, the speed of integer to string conversion is of more importance than memory usage.

If a variable is initially assigned a value of <4 bytes, it receives 3 bytes of "permanent" memory. If it is then assigned a value of between 4 and 7 bytes, it receives 7 bytes of "permanent" memory. If the variable is never assigned a value outside of the range 0-255 (or hexadecimal 0x0-0xff) and it is not a local variable of a recursive function, memory is allocated for it at most 2 times in the lifetime of the script.
(As with the return value of VarSetCapacity, the above excludes the extra byte allocated for the null-terminator.)
Quote:
If no is there maybe som low level technique for comparision without the need of time taking conversion ?
Only machine code functions.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 8th, 2008, 12:04 am 
Offline

Joined: February 26th, 2006, 9:45 am
Posts: 55
Location: Recife Brazil
Quote:
What makes you think AHK variable's are 64 bytes long?

PhiLho wrote:
AutoHotkey's variables are 64 byte long! But each digit is recorded as its Ascii value.

In AHK doc´s you read: AutoHotkey has no explicitly defined variable types; all variables are stored as character strings.However, a variable containing only digits (with an optional decimal point) is automatically converted to a number when a math operation or comparison requires it. Conversely, the result of a math operation is converted back to a string when it needs to be stored in a variable

So the truth is that there is no initial string lenght for variables provided in AHK´s source.
Makes more sence, because I saw variables string lenght using hexediter searching in RAM just as in script. I just can´t imagine what PhiLho meant to say.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 20th, 2008, 5:31 pm 
Offline
User avatar

Joined: December 26th, 2005, 4:40 pm
Posts: 8776
Chris wrote:
I've also added the following to the DllCall page in the documentation:

In v1.0.46.08+, even faster performance can be achieved by looking up the function's address beforehand. For example:
Code:
MulDivProc := DllCall("GetProcAddress", uint, DllCall("GetModuleHandle", str, "kernel32"), str, "MulDiv")  ; But if the DLL isn't yet loaded, first use LoadLibrary.
Loop 500
    DllCall(MulDivProc, int, 3, int, 4, int, 3)

Thanks for the idea.


I feel it is bit misleading Mr.Chris, LoadLibrary() and GetModuleHandle() return the same value.

Code:
; But if the DLL isn't yet loaded, first use LoadLibrary.


I think this would be better: But if the DLL isn't yet loaded, use LoadLibrary instead

It would be great if AHK has a built-in LoadDllFunction() - an example code:

Code:
#Persistent

LoadDllFunction( DllFile="", FunctionName_Or_Ordinal=0 ) {

 If ! ( hModule := DllCall( "GetModuleHandle", Str,DllFile ) )
        hModule := DllCall( "LoadLibrary", Str,DllFile )

 ArgType := ( FunctionName_Or_Ordinal+0 <> "" ) ? "UInt" : "Str"
 Return DllCall( "GetProcAddress", UInt,hModule, ArgType,FunctionName_Or_Ordinal )
}

; Example Call 1


HashData := LoadDllFunction( "shlwapi.dll", "HashData" )
SetFormat, Integer, Hex
DllCall( HashData, Str,"The Quick Brown Fox", UInt,19, UIntP,Hash, UInt,4 )
MsgBox, % Hash+0


; Example Call 2

RunDialog := LoadDllFunction( "Shell32.dll", 61 )
DllCall( RunDialog, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0 )


:)


Last edited by SKAN on June 9th, 2010, 9:40 am, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 20th, 2008, 9:52 pm 
Offline

Joined: January 12th, 2008, 7:45 pm
Posts: 131
Erhm, didn't read this thread, and I don't know if this has been mentioned. But anyways, as I've experienced AHK it's faster to define the type of the of the variable in between two "s...

e.g.
Code:
DllCall("SetPixel",Int,hdc,UChar,x,UChar,y,Int,color)

is slower than
Code:
DllCall("SetPixel","Int",hdc,"UChar",x,"UChar",y,"Int",color)

The difference is that i put the value type between "s, so that it's treaded as a string...


Code:
SetBatchLines -1
Gui,Add,Text, vmy_text x10 y270 h60 w250, Please wait.
Gui,Show,w259 h290, ColorPicker
hDC := DllCall("GetDC", "UInt", WinExist("ColorPicker"))
x := 0
b := 0 ; blue component slider value
start_time := a_tickCount
Loop 255
{   
   x++
   y = 0
   loop 255
      {
     y++
      color :=  (b << 16) | (y << 8) | x
      DllCall("SetPixel","Int",hdc,"UChar",x,"UChar",y,"Int",color)
      }
}
needed_time := (a_tickCount - start_time)/1000

GuiControl,,my_text, This drawing is done in %needed_time% seconds
Return

GuiClose:
ExitApp


with this i got 0.797 vs. the default code; 1.453... well it's atleast faster than not putting the value types between "s...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 20th, 2008, 10:25 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Ice_Tea, that is typically only true if Int, etc. are empty variables and you have not used #NoEnv, as referencing Int would then perform an environment variable lookup.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 20th, 2008, 10:48 pm 
Offline

Joined: January 12th, 2008, 7:45 pm
Posts: 131
Lexikos wrote:
Ice_Tea, that is typically only true if Int, etc. are empty variables and you have not used #NoEnv, as referencing Int would then perform an environment variable lookup.


Oh, thanks for clearing that out to me :D.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 20th, 2008, 11:47 pm 
Offline

Joined: February 26th, 2006, 9:45 am
Posts: 55
Location: Recife Brazil
Quote:
Ice_Tea, that is typically only true if Int, etc. are empty variables and you have not used #NoEnv, as referencing Int would then perform an environment variable lookup.


Hoho..... HelpFile tells me, use #NoEnv to avoid checking empty variables to see if they are environment variables.
My example doesn´t use empty variables neither #NoEnv... But it should! Or quote the parameter types like Ice_Tea wrote.
Script speeds up on the PC I'm using at the moment from 3 to 1,32 seconds
Reading HelpFile further it says "It also improves DllCall's performance when unquoted parameter types are used (e.g. int vs. "int")"
So HelpFile should write: use #NoEnv to avoid checking empty variables to see if they are environment variables and to improve DllCall's performance when unquoted parameter types are used (e.g. int vs. "int") and to prevents script bugs caused by environment variables whose names unexpectedly match variables used by the script.
Further HelpFile is confusing about explaining use of quotes in DllCall, I can´t find something written like -using quotes gives better performance-
especialy in the part talking about performance, but in the examples it is used yes. Maybe here too should be mencioned the use of #NoEnv
Several times I tried use of quotes, but it didn´t make any difference.
Now I now; I used #NoEnv.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 93 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: maul.esel and 2 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group