 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Debuggerizer Guest
|
Posted: Fri Dec 19, 2008 4:00 am Post subject: Timers don't work |
|
|
Here's the test code I used:
| Code: | cycles = 0
SetTimer, TestTimer, 10
TestTimer:
cycles++
Tooltip, %time% |
With an interval of 10 milliseconds for each run, it should run the loop 100 times per second. I ran the loop for approximately 60 seconds (give or take one or two seconds), so the number I should have gotten should have been within the range of 5,900-6,100. However, I only got about 3,700 after one minute. Instead of getting 100 iterations per second, I got about 61.6 iterations per second. 1,000 milliseconds / 61.6 cycles = 16.23 milliseconds per cycle. So, instead of getting one cycle every 10 seconds, I'm getting one cycle every 16.23 seconds.
Does anyone else have the same problem? |
|
| Back to top |
|
 |
Debuggerizer Guest
|
Posted: Fri Dec 19, 2008 4:18 am Post subject: |
|
|
Also, I tried changing from 10 milliseconds to 20, so that I would get 50 cycles per second. I should have gotten around 3,000 cycles after a minute. I only got 1,900. I tried changing from 20 to 50, so that I would get 20 cycles per second. I should have gotten 1,200 cycles after a minute, but I got 960. I changed it to 100 milliseconds per cycle, so that I would get 600 cycles after a minute. I got 540 cycles. It's consistently slower than it should be.
Also, I'm running XP with Service Pack 3, if it matters. |
|
| Back to top |
|
 |
Ice_Tea
Joined: 12 Jan 2008 Posts: 131
|
Posted: Fri Dec 19, 2008 4:18 am Post subject: |
|
|
Hey, are you running the script with "SetBatchLines, -1" without that the script won't run as fast as it could...
Another note worth mentioning is that timers aren't that precise... as I recall it wouldn't count more precise than aprox 16ms (depending on hardware/state of the pc etc), but it's just a rough note that I don't remember really well...
Basically there is not much you can do about timers, they will not be precise... I've gone through this before...
PS it's not a bug... _________________
 |
|
| Back to top |
|
 |
wrong_section_alert Guest
|
Posted: Fri Dec 19, 2008 4:23 am Post subject: |
|
|
Moderator: This should be in Ask for Help. Just because Debuggerizer does not undertand the limits of timers, is not a good reason to post (or leave it here as) a bug report. IMO
| Help wrote: | Currently, timers cannot run much more often than every 10ms on Windows XP/2000/NT and about 55ms on Windows 9x. Specifying a Period less than this will usually result in an actual interval of 10 or 55 (but this policy could change in future versions so shouldn't be relied upon).
A timer might not be able to run as often as specified under the following conditions:
Other applications are putting a heavy load on the CPU.
The timer subroutine itself takes longer than its own period to run, or there are too many other competing timers (altering SetBatchLines may help).
The timer has been interrupted by another thread, namely another timed subroutine, hotkey subroutine, or custom menu item (this can be avoided via Critical). If this happens and the interrupting thread takes a long time to finish, the interrupted timer will be effectively disabled for the duration. However, any other timers will continue to run by interrupting the thread that interrupted the first timer.
The script is uninterruptible as a result of Critical or Thread Interrupt/Priority. During such times, timers will not run. Later, when the script becomes interruptible again, any overdue timer will run once as soon as possible and then resume its normal schedule. |
|
|
| Back to top |
|
 |
Debuggerizer Guest
|
Posted: Fri Dec 19, 2008 5:24 am Post subject: |
|
|
I'm using Windows XP, so, according to the information about SetTimer, 10ms should work. Furthermore, the other settings that I used (20, 50, 100) should have worked. My normal CPU usage is around 1-5% and I've got a dual core 2.81 Ghz Intel CPU. There shouldn't be any problems because of an excessive load. It should be clear that the timer subroutine I used takes no longer than 16 milliseconds, as I demonstrated in the first example. So, when I gave it 20 milliseconds to complete the task, it should have finished in exactly 20 milliseconds. I wasn't running any other ahk scripts at the time, there should be no interference from that.
Basically, I ran this test under ideal conditions, and I didn't get what was claimed:
| Quote: | | Period: Creates or updates a timer using this parameter as the number of milliseconds that must pass since the last time the Label subroutine was started. When this amount of time has passed, Label will be run again (unless it is still running from the last time). |
My test at 100 milliseconds per cycle should have worked. There should definitely have been enough time to complete the subroutine, and then start it again right on time. However, the timer seems to add anywhere from 6-12 ms to the original time.
Original setting - Real setting
10 ms spacespa- 16.23 ms
20 ms spacespa- 31.58 ms
50 ms spacespa- 62.5 ms
100 ms spacesp- 111.11 ms
There's no mention in the description of the timer that it will add 6-12 ms to the actual time that you set. |
|
| Back to top |
|
 |
Slanter
Joined: 28 May 2008 Posts: 739 Location: Minnesota, USA
|
Posted: Fri Dec 19, 2008 5:38 am Post subject: |
|
|
Try posting the exact code you used... the one you did post won't do anything... _________________ Unless otherwise stated, all code is untested
(\__/) This is Bunny.
(='.'=) Cut, copy, and paste bunny onto your sig.
(")_(") Help Bunny gain World Domination.
Last edited by Slanter on Fri Dec 19, 2008 5:56 am; edited 2 times in total |
|
| Back to top |
|
 |
Debuggerizer Guest
|
Posted: Fri Dec 19, 2008 5:42 am Post subject: |
|
|
Ah, sorry, my mistake.
| Code: | cycles = 0
SetTimer, TestTimer, 10
TestTimer:
cycles++
Tooltip, %cycles% |
|
|
| Back to top |
|
 |
Ice_Tea
Joined: 12 Jan 2008 Posts: 131
|
Posted: Fri Dec 19, 2008 5:43 am Post subject: |
|
|
As I've understood, the weird delay i is not a set value, the value is variable, and as I recall that value is not the same on all PCs... you shouldn't rely on settimer for exact calculation, as it is not predictable... it rounds up just like A_Tickcount, I believe..
| Code: | #Persistent
;SetBatchLines, -1 ; this shouldn't be commented, but w/e...
cycles = 0
SetTimer, TestTimer, 10
TestTimer:
cycles++
Tooltip, %cycles% |
Fix'd (as in the script running, but the problem still exists) _________________
 |
|
| Back to top |
|
 |
Slanter
Joined: 28 May 2008 Posts: 739 Location: Minnesota, USA
|
Posted: Fri Dec 19, 2008 5:57 am Post subject: |
|
|
Just tested this using the following script. Did tests over 60 seconds (exactly 60 seconds...). Took ~0.4% longer than expected on 10ms, ~0.2% longer than expected on 100ms, ~0.1% longer than expected on 1000ms. I am running Win2k Pro on a PoS computer from the 90's, while running firefox, windows media player, and several other programs.
I see no problem here
| Code: | #Persistent
Projected = 0
Time := 1000
SetBatchLines, -1
SetTimer, Stop, 60000
Start := A_TickCount
SetTimer, TestTimer, %Time%
Return
TestTimer:
Projected+=Time
Tooltip, % ((A_TickCount - Start) / Projected) " times longer than expected"
Return
Stop:
SetTimer, TestTimer, Off
Return |
_________________ Unless otherwise stated, all code is untested
(\__/) This is Bunny.
(='.'=) Cut, copy, and paste bunny onto your sig.
(")_(") Help Bunny gain World Domination. |
|
| Back to top |
|
 |
derRaphael
Joined: 23 Nov 2007 Posts: 841 Location: ~/.
|
Posted: Fri Dec 19, 2008 9:21 am Post subject: |
|
|
here we have a hi res benchmark script build with my benchmarking framework ...
(abs framework from http://www.autohotkey.com/forum/viewtopic.php?t=35354)
| ABS 1.0 wrote: | ---------------------------
Benchmark Results 5 Runs with 10 Rounds
---------------------------
SetBatchLines, 20
Critical, Off / Process Priority Normal
0.100033 sec (Standard SetTimer,Label,-10)
SetBatchLines, -1
Critical, Off / Process Priority Normal
0.175800 sec (Standard SetTimer,Label,-10)
SetBatchLines, -1
Critical,Off / Process Priority High
0.183413 sec (Standard SetTimer,Label,-10)
---------------------------
OK
--------------------------- |
this is the abs code used (code to benchmark is red):
| Code: | ; QueryPerfomanceCounter - Benchmark Skript
; Part of Official PlayAHK.com xFast-Exec Benchmark Skript
; Version 1.0 (w) 2008 by DerRaphael / zLib License Style Release
DllCall("QueryPerformanceFrequency", "Int64*", BenchMark[F])
; BENCHMARK START
; Äußere Runden / Outer Rounds
@OR := 5
; Innere Läufe / Inner Runs
@IR := 10
; Start Benchmark
Loop,3
{
Setting := A_Index
SetBatchLines, 20
Critical, Off
Process, Priority,, Normal
if (A_Index = 1) {
CurrentSettings := "SetBatchLines, 20`nCritical, Off / Process Priority Normal"
} else if (A_Index = 2) {
CurrentSettings := "SetBatchLines, -1`nCritical, Off / Process Priority Normal"
SetBatchLines, -1
Process, Priority,, Normal
} else if (A_Index = 3) {
CurrentSettings := "SetBatchLines, -1`nCritical,Off / Process Priority High"
SetBatchLines, -1
Process, Priority,, H
}
OfficialResult .= "`n" CurrentSettings "`n"
tooltip,% currentsettings, 10, 10
Loop, % @OR
{
A_Run := A_Index
Loop, % @IR
{
; Zähler der auszuführenden Tests / Count of Tests to accomplish
A_Test := 0
; SOBM***********************************************************************************
A_Test+=1
if !StrLen(Desc[%A_Test%])
Desc[%A_Test%] := "Standard SetTimer,Label,-10"
DllCall("QueryPerformanceCounter", "Int64 *", BenchMark[SC]) ; StartCounter
; Hier komme hinein, was getestet wird / Insert what needs to be tested
counter := 0
Loop,
if (counter=10) ; expected ideal time is 100 ms
break
Else {
SetTimer,tst_iteration,-10
}
; Hier ist zuende, was getestet wurde / End of whatever has been tested
DllCall("QueryPerformanceCounter", "Int64 *", BenchMark[EC]) ; EndCounter
Result[%A_Test%][%A_Run%][%A_Index%] := (BenchMark[EC] - BenchMark[SC]) / BenchMark[F]
; ***********************************************************************************EOBM
}
; Zeiten mitteln / Average Timings
Loop, % A_Test
{
A_Test := A_Index
Result[%A_Test%][%A_Run%] := 0
Loop, % @IR
Result[%A_Test%][%A_Run%] += Result[%A_Test%][%A_Run%][%A_Index%]
Result[%A_Test%][%A_Run%] /= @IR
}
}
; BENCHMARK END
; Resultate ermitteln / Calculate Results
Loop, % A_Test
{
A_Test := A_Index
Result[%A_Test%] := 0
Loop, % A_Run
Result[%A_Test%] += Result[%A_Test%][%A_Index%]
Result[%A_Test%] /= @OR
OfficialResult .= Result[%A_Test%] " sec`t(" Desc[%A_Test%] ")`n"
}
}
SetBatchLines, 20
Critical, Off
Process, Priority,, Normal
MsgBox,64,Benchmark Results %@OR% Runs with %@IR% Rounds, %OfficialResult%
ExitApp
tst_iteration:
counter += 1
return |
i might be wrong, but to me it looks as if the timer works perfectly well under standard circumstances. maybe someone else can also benchmark this one and post result msgbox (1ghz / win2k sp4 / ahk 1.0.48 beta)
try rerunning the script multiple times as windows caches the binary (later runs result in more acurate timings)
greets
dR _________________
All scripts, unless otherwise noted, are hereby released under CC-BY |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10716
|
Posted: Wed Dec 31, 2008 5:11 am Post subject: |
|
|
I've clarified the SetTimer documentation to indicate that 15ms is the minimum interval on some PCs. In the future it could probably use some more clarification -- perhaps even a mention of the timeBeginPeriod technique mentioned in other topics.
Thanks. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Wed Dec 31, 2008 3:36 pm Post subject: |
|
|
| It was discussed more than 3 years ago, too. Most modern PC’s (chipsets?) seem to have a HW tick period of 15.6ms, and accordingly, the timer period is not what you request, but the closest multiple of 15.6ms (at least 15.6ms for larger than 0 periods). If you need better resolution, you can always use the multimedia (performance) timers, although I experienced bad crashes with them in complex scripts. |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10716
|
Posted: Sat Jan 03, 2009 4:04 pm Post subject: |
|
|
Thanks for that info, which I've applied to the Sleep and SetTimer pages. I also added the following wording:
| Quote: | Due to the granularity of the OS's time-keeping system, Delay is typically rounded up to the nearest multiple of 10 or 15.6 milliseconds (depending on the type of hardware and drivers installed). For example, a delay between 1 and 10 (inclusive) is equivalent to 10 or 15.6 on most Windows NT/2000/XP systems. To achieve a shorter delay [see the following example]:
...
| Code: | ; The following is a working example that demonstrates how to sleep for less time than the
; normal 10 or 15.6 milliseconds.
; NOTE: While a script like this is running, the entire operating system and all applications are
; affected by timeBeginPeriod below.
SetBatchLines -1 ; Ensures maximum effectiveness of this method.
SleepDuration = 1 ; This can sometimes be finely adjusted (e.g. 2 is different than 3) depending on the value below.
TimePeriod = 3 ; Try 7 or 3. See comment below.
; On a PC whose sleep duration normally rounds up to 15.6 ms, try TimePeriod=7 to allow
; somewhat shorter sleeps, and try TimePeriod=3 or less to allow the shortest possible sleeps.
DllCall("Winmm\timeBeginPeriod", uint, TimePeriod) ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
Iterations = 50
StartTime := A_TickCount
Loop %Iterations%
DllCall("Sleep", UInt, SleepDuration) ; Must use DllCall instead of the Sleep command.
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod) ; Should be called to restore system to normal.
MsgBox % "Sleep duration = " . (A_TickCount - StartTime) / Iterations |
|
Things like the above probably have been discovered and explored in other topics; so improvements are welcome.
Edit: Optimized the code.
Last edited by Chris on Sat Jan 03, 2009 5:28 pm; edited 1 time in total |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Sat Jan 03, 2009 4:48 pm Post subject: |
|
|
I have been using a much simpler method: It sleep ms milliseconds, by playing an inaudible sound (1Hz). At least in my Vista PC there is no large CPU load, but older PC's could actually use the CPU to generate the sound, so I am not sure, if it is a good substitute in every situation.
The script above gives the same result if I remove the timeBeginPeriod and timeEndPeriod dll calls, in my Vista 32 PC. It looks like the Sleep dll call uses finer timer resolution than the Sleep AHK command. Or, it uses some averaging process. MSDN does not say. |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10716
|
Posted: Sat Jan 03, 2009 5:24 pm Post subject: |
|
|
| Laszlo wrote: | | I have been using a much simpler method: It sleep ms milliseconds, by playing an inaudible sound (1Hz). | Creative technique. For some reason on my PC, it doesn't seem to sleep at all (or inconsistently) if the frequency is below 40 or so. The following displays 0.000000 unless I raise the frequency:
| Code: | SetBatchLines -1 ; Ensures maximum effectiveness of this method.
Iterations = 50
StartTime := A_TickCount
Loop %Iterations%
SoundBeep 30, 10
MsgBox % "Sleep duration = " . (A_TickCount - StartTime) / Iterations |
| Laszlo wrote: | | The script above gives the same result if I remove the timeBeginPeriod and timeEndPeriod dll calls, in my Vista 32 PC. It looks like the Sleep dll call uses finer timer resolution than the Sleep AHK command. Or, it uses some averaging process. MSDN does not say. | Interesting. I believe AutoHotkey uses SetTimer() to implement the Sleep command except for Sleep 0 (and except for short sleeps on Windows 9x). Perhaps a future version of AutoHotkey should use Sleep() instead of SetTimer() when the specified duration is very short, perhaps under 10ms (at least if the OS is Vista or later). On the other hand, that seems likely to alter the behavior of a lot of existing scripts, so it might be best to defer it to AutoHotkey v2.
In the meantime, a new parameter could be added to Sleep, maybe TimePeriod:
If new parameter missing (or zero?), use existing/old behavior.
If present, use timeBeginPeriod + Sleep + timeEndPeriod.
However, that mode could have side effects because it would affect the entire OS and all applications during the duration of the Sleep. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|