Jump to content


Photo

QPX() & Delay() :: Based on QueryPerformanceCounter()


  • Please log in to reply
9 replies to this topic

#1 SKAN

SKAN
  • Administrators
  • 9062 posts

Posted 10 December 2009 - 12:27 PM

QPX()
 
QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}
 
Returns value will be in Seconds like 1.234567,
Where, Red is Seconds, Green is Milliseconds and Blue is Thousandth of Millisecond

Example:
 
SetBatchLines -1

;;** Basic Usage **
QPX( True ) ; Initialise Counter
Sleep 1000
Ti := QPX( False ) ; Retrieve Time consumed ( & reset internal vars )

MsgBox, 0, Sleep 1000, %Ti% seconds

;;** Extended Usage **
While QPX( 1000 ) ; Loops 1000 times and keeps internal track of the total time
Tooltip %A_Index%
Ti := QPX() ; Retrieve Avg time consumed per iteration ( & reset internal vars )

MsgBox, 0, Avg Time Taken for ToolTip, %Ti% Seconds / Iteration
 
Delay()
 
MsgBox, % Delay( 0.008 ) ; Delay for 8ms

Delay( D=0.001 ) { ; High Resolution Delay ( High CPU Usage ) by SKAN | CD: 13/Jun/2009
Static F ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 13/Jun/2009
Critical
F ? F : DllCall( "QueryPerformanceFrequency", Int64P,F )
DllCall( "QueryPerformanceCounter", Int64P,pTick ), cTick := pTick
While( ( (Tick:=(pTick-cTick)/F)) <D ) {
DllCall( "QueryPerformanceCounter", Int64P,pTick )
Sleep -1
}
Return Round( Tick,3 )
}


#2 Mystiq

Mystiq
  • Members
  • 83 posts

Posted 13 August 2010 - 07:36 AM

Thanks for this!

I tried to play around with the PerfCounter myself but got so inconsistent results that i decided to use your function instead.

Below function is meant for testing function performance and it supports up to 8 parameters which is easily extendable.

; QPXF(<passes>, <function name>, <parameter 1>, ..., <parameter 8>)
QPXF(_P, _F, _1="", _2="", _3="", _4="", _5="", _6="", _7="", _8=""){
	If not (IsFunc(_F) || _P > 0)
		Return
	Loop, %_P%
	{
		If (_1 = "")
			QPX(True), %_F%()
		else If (_8 != "")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7,_8)
		else If (_7 != "")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7)
		else If (_6 != "")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6)
		else If (_5 != "")
			QPX(True), %_F%(_1,_2,_3,_4,_5)
		else If (_4 != "")
			QPX(True), %_F%(_1,_2,_3,_4)
		else If (_3 != "")
			QPX(True), %_F%(_1,_2,_3)
		else If (_2 != "")
			QPX(True), %_F%(_1,_2)
		else If (_1 != "")
			QPX(True), %_F%(_1)
		T += QPX(False)
	}
Return T / _P
}

Example:

;-- run "Delay(0.5)" once
MsgBox % QPXF(1, "Delay", 0.5)

;-- same as:
QPX(true)
Delay(0.5)
MsgBox % QPX()

;-- run "Delay(0.5)" 5 times and return average
MsgBox % QPXF(5, "Delay", 0.5)

Comments are welcome. :)

#3 Relayer

Relayer
  • Members
  • 104 posts

Posted 13 August 2010 - 04:03 PM

hummmmm...

When I try to call Skan's Delay function I get an error from AHK saying functions cannot contain functions. I'm staring at it and it's not making sense. It is complaining about the line with the while statement.

#NoEnv
Delay(5)
msgbox, What the?
ExitApp

Delay( D=0.001 ) {  ; High Resolution Delay ( High CPU Usage )  by SKAN  | CD: 13/Jun/2009
 Static F           ; www.autohotkey.com/forum/viewtopic.php?t=52083     | LM: 13/Jun/2009
 Critical
 F ? F : DllCall( "QueryPerformanceFrequency", Int64P,F )
 DllCall( "QueryPerformanceCounter", Int64P,pTick ), cTick := pTick
 While( ( (Tick:=(pTick-cTick)/F)) <D ) {
   DllCall( "QueryPerformanceCounter", Int64P,pTick )
   Sleep -1
 }
Return Round( Tick,3 )
}


#4 Guests

  • Guests

Posted 13 August 2010 - 04:23 PM

You probably don't have the latest version of AHK if it sees While() as a function.
<!-- m -->http://www.autohotkey.com/download/<!-- m -->

#5 Relayer

Relayer
  • Members
  • 104 posts

Posted 13 August 2010 - 05:05 PM

Thanks... that was it. I thought I had the latest but didn't. Silly me!

#6 Frankie

Frankie
  • Members
  • 2930 posts

Posted 18 October 2010 - 07:47 PM

Thanks SKAN!
This could be very useful in timing scripts aswell as comparing different AHK versions.

#7 fincs

fincs
  • Fellows
  • 1532 posts

Posted 18 October 2010 - 08:17 PM

Here's an AutoHotkey_L version of Mystiq's QPXF() function (couldn't resist):
; QPXF(<passes>, <function name>, <parameters>...)
QPXF(_P, _F, _A*)
{
   if !IsFunc(_F) || _P > 0
      return
   Loop, %_P%
      QPX(true), %_F%(_A*), T += QPX(false)
   return T / _P
}


#8 SKAN

SKAN
  • Administrators
  • 9062 posts

Posted 19 October 2010 - 12:05 AM

Here's an AutoHotkey_L version of Mystiq's QPXF() function (couldn't resist):


Arbitrary number of parameters?! :O
Nice demo... Thanks.. :)

Meanwhile, I have to clarify:

There is no need for a sub wrapper for QPX().
QPX() was specifically written to test average speed of a user defined function. I guess the example in title post is not clear enough!.

While QPX( 100000 )
 Rnd := Random()
ti := QPX(0)

MsgBox, 0, 100`,000 iterations,  % "Avg. time taken for a Random Number:`t" ti " seconds"

Random() {
 Random,Rnd, 0x1,0xFFFFFFFF
Return Rnd
}


#9 Lexikos

Lexikos
  • Administrators
  • 8855 posts

Posted 04 December 2010 - 11:32 PM

While QPX( 100000 )
Rnd := Random()
ti := QPX(0)

When I run that, I get a different result each time. There are two ways to make it more consistent:
[*:2ujr4xs1]Increase the number of iterations. If I run 100000*10 iterations, I get the same result every time.
[*:2ujr4xs1]Add SetBatchLines -1 so that the benchmark is less dependent on background activity (and completes faster to boot).By my estimate, two thirds (AutoHotkey_L) or three quarters (AutoHotkey Basic) of the time required for the benchmark is actually spent in QPX() and the loop itself. It is neither necessary nor wise to call the timing function once in every iteration. Instead, capture only the start and end times, then calculate the difference and divide by the number of iterations. Generally if the accuracy of A_TickCount isn't adequate, the test is too short and would not get consistent results with QPC anyway.

For example:
i := 10000000
SetBatchLines -1
begin := A_TickCount
Loop % i
{
    Rnd := Random()
}
ti := (A_TickCount-begin)/i

MsgBox, 0, %i% iterations,  Avg. time taken for a Random Number:`t%ti% ms

Random() {
    Random,Rnd, 0x1,0xFFFFFFFF
    Return Rnd
}
I estimate around 5% of the time is spent executing Loop % i { } and the rest spent executing Rnd := Random().

#10 emmanuel d

emmanuel d
  • Members
  • 494 posts

Posted 24 January 2011 - 06:31 PM

Then this would be a good example:
i := 10000000
SetBatchLines -1
;------------------------------------------the loop itself
begin := A_TickCount
Loop % i {
    L1:=1
	
	}
L := (A_TickCount-begin)/i
;------------------------------------------the built in function
begin := A_TickCount
Loop % i {
    L1:=1
    res1:=mod(15,4)
	}
ti := (A_TickCount-begin)/i-L
;--------------------------------------------my function
begin := A_TickCount
Loop % i {
    L1:=1
    res2:=mod_(15,4)
	}
ti2 := (A_TickCount-begin)/i-L
;---------------------------------------------on the fly calculation
begin := A_TickCount
Loop % i {
    L1:=1
    res2 := 15-15//4*4
	}
ti3 := (A_TickCount-begin)/i-L
;---------------------------------------------------
MsgBox, 0, %i% iterations,  Avg. time for Loop:`t`t`t%L% ms`nAvg. time for Mod():`t`t`t%ti% ms`nAvg. time for Mod_():`t`t%ti2% ms`nAvg. time for manual Mod calculation:`t%ti3% ms

Mod_(Nr,Div) {
	return Nr-Nr//Div*Div
}
It shows the difference between a built in function, a created function, and calculation on the fly.
it excludes the loop from the time.