MicroTimer - Sub-10ms timers for AHK (C# DLL)

Post your working scripts, libraries and tools for AHK v1.1 and older
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

04 Apr 2017, 08:44

evilC wrote:Hmm, it seems the old QueueTimer code us WAY less CPU intensive. I see 0% CPU usage for that with a 10s sleep
I tested it again, it works very good w.r.t accuracy and cpu usage, but sometimes the script just dies, and silently exits.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

04 Apr 2017, 08:52

Both function (QPC and GetSystemTimePreciseAsFileTime) used 12.5 % CPU (not compiled) for a 10 seconds test
- If I add sleep -1 to the while loop the CPU is still using 12.5 %
- As soon as I add sleep 1 to the while-loop the CPU goes down to under 1 % but its not precise like without sleep
msdn wrote:However, the accuracy depends on the priority of your thread, the priority of other threads, and whether interrupt service routines (ISRs) are running.
+ AutoHotkey itself (as long lexikos tell me that Im wrong :P )
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

06 Apr 2017, 05:02

Found some more interesting stuff about Timers:

Timers, Timer Resolution, and Development of Efficient Code
Timer-Resolution.docx (msdn - .docx)

with reference to SetWaitableTimerEx function (msdn)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

06 Apr 2017, 09:24

Here is a C# implementation that uses WaitableTimers:
Code shamelessly stolen from here

AHK

Code: Select all

#SingleInstance force
#Persistent

#include CLR.ahk

asm := CLR_LoadLibrary("bin\debug\WaitableTimer.dll")

wt := asm.CreateInstance("WaitableTimer")
MyTimer := wt.Create(Func("Test"), 1)
MyTimer.Start()
Sleep, 10000
MyTimer.Stop()

return

Test(){
	ToolTip % A_TickCount
}

^Esc::
	ExitApp
C#

Code: Select all

using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class WaitableTimer
{
    public TimerInstance Create(dynamic _callback, int _period)
    {
        return new TimerInstance(_callback, _period);
    }

    public class TimerInstance
    {
        dynamic callback;
        int period;
        Thread timerThread;
        _WaitableTimer waitableTimer = new _WaitableTimer(false, "");

        public TimerInstance(dynamic _callback, int _period)
        {
            callback = _callback;
            period = _period;
        }

        public void Start()
        {
            timerThread = new Thread(new ThreadStart(TimerLoop));

            timerThread.Start();
        }

        public void Stop()
        {
            timerThread.Abort();
        }

        public void TimerLoop()
        {
            waitableTimer.Set(-0L, period);
            while (true)
            {
                waitableTimer.WaitOne();
                callback();
            }
        }
    }

    public class _WaitableTimer : WaitHandle
    {
        [DllImport("kernel32.dll")]
        static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, [MarshalAs(UnmanagedType.Bool)] bool fResume);

        public _WaitableTimer(bool manualReset = true, string timerName = null)
        {
            this.SafeWaitHandle = CreateWaitableTimer(IntPtr.Zero, manualReset, timerName);
        }

        public void Set(long dueTime, int period)
        {
            if (!SetWaitableTimer(this.SafeWaitHandle, ref dueTime, period, IntPtr.Zero, IntPtr.Zero, false))
            {
                throw new Win32Exception();
            }
        }
    }
}
DLL is attached.
Attachments
WaitableTimer.zip
(2.95 KiB) Downloaded 220 times
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

06 Apr 2017, 10:13

It seems to work well evilC. Low cpu (not noticable) and accurate.
I measured ~10 ms off on 10000 1 ms periods.

Code: Select all

#SingleInstance force
#Persistent
SetBatchLines,-1
asm := CLR_LoadLibrary(A_ScriptDir "\WaitableTimer.dll")
global period:=1
wt := asm.CreateInstance("WaitableTimer")
MyTimer := wt.Create(Func("Test"), period)
MyTimer.Start()
Sleep, 100000
MyTimer.Stop()

return

Test(){
	static ctr:=0, tic1:=0, periods:=10000
	if !ctr
		DllCall("QueryPerformanceCounter", "Int64P", tic1)
	ctr++
	if (ctr=periods){
		DllCall("QueryPerformanceCounter", "Int64P", tic2)
		DllCall("QueryPerformanceFrequency", "Int64P", f)
		MsgBox, % "Elapsed time:`t" round((tic2-tic1)*1000/f) " ms.`nExpected:`t`t" period*periods " ms."
		Exitapp
	}
	return
}

^Esc::
	ExitApp
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

06 Apr 2017, 10:54

@jNizM did you try SetWaitableTimerEx function with AHK-dllcall? I fail, I get A_LastError=5 (Access denied), this is what I tried, with doing as little reading in the relevant documentation as possible :lol: ,

Code: Select all

#Persistent
lpTimerAttributes:=0
lpTimerName:=0
dwFlags:=0
dwDesiredAccess:=0																; can be "str" to it seems. i.e., "str", "lpTimerName"
hTimer:=DllCall("Kernel32.dll\CreateWaitableTimerEx", "Uptr", lpTimerAttributes, "UPtr", lpTimerName, "Uint", dwFlags, "Uint", dwDesiredAccess)
MsgBox, % hTimer  "`n" ErrorLevel  "`n" A_LastError

ms:=35							; milliseconds
VarSetCapacity(lpDueTime,8,0)
NumPut(- 100 * 10000 * ms,lpDueTime,4,"Int")

lPeriod:= 0 					; 0 run once ?
pfnCompletionRoutine:= RegisterCallback("f")
lpArgToCompletionRoutine:=0		; Args to f() ?
WakeContext:=0					; Can be set to make the timer wake the computer from sleep it seems.
TolerableDelay:=0

r:=DllCall("Kernel32.dll\SetWaitableTimerEx", "Uptr", hTimer, "UPtr", &lpDueTime, "Int", lPeriod, "Ptr", pfnCompletionRoutine, "Ptr", lpArgToCompletionRoutine, "Ptr", WakeContext, "Uint", TolerableDelay)
MsgBox, % r  "`n" ErrorLevel  "`n" A_LastError

f(){
	MsgBox, % "hi from setwaitabletimer."
	exitapp
}
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

07 Apr 2017, 01:58

Code: Select all

TIMER_ALL_ACCESS   := 0x1F0003
TIMER_MODIFY_STATE := 0x0002
TIMER_QUERY_STATE  := 0x0001

if !(hTimer := DllCall("CreateWaitableTimerEx", "ptr", 0, "ptr", 0, "uint", 0, "uint", TIMER_ALL_ACCESS, "ptr"))
    MsgBox % "CreateWaitableTimerEx failed with Error: " A_LastError
MsgBox % "Handle to timer object: " hTimer

/*
; stuff here...

if !(DllCall("SetWaitableTimerEx", "ptr", hTimer, "int64*", DueTime, "int", Period, "ptr", CompletionRoutine, "ptr", ArgToCompletionRoutine, "ptr", WakeContext, "uint", TolerableDelay))
    MsgBox % "SetWaitableTimerEx failed with Error: " A_LastError

; more stuff here...
*/

DllCall("CloseHandle", "ptr", hTimer)
But I dont know how to use SetWaitableTimerEx to get the timer working when needed
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

08 Apr 2017, 00:00

nice to have true sub ms timer, but MicroTimer uses too much cpu than native ahk's settimer.
can't handle the timer with games.. :(
AutoHotkey & AutoHotkey_H v1.1.22.07
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

08 Apr 2017, 00:16

jNizM wrote:

Code: Select all

TIMER_ALL_ACCESS := 0x1F0003
TIMER_MODIFY_STATE := 0x0002
TIMER_QUERY_STATE := 0x0001

if !(hTimer := DllCall("CreateWaitableTimerEx", "ptr", 0, "ptr", 0, "uint", 0, "uint", TIMER_ALL_ACCESS, "ptr"))
 MsgBox % "CreateWaitableTimerEx failed with Error: " A_LastError
MsgBox % "Handle to timer object: " hTimer

/*
; stuff here...

if !(DllCall("SetWaitableTimerEx", "ptr", hTimer, "int64*", DueTime, "int", Period, "ptr", CompletionRoutine, "ptr", ArgToCompletionRoutine, "ptr", WakeContext, "uint", TolerableDelay))
 MsgBox % "SetWaitableTimerEx failed with Error: " A_LastError

; more stuff here...
*/

DllCall("CloseHandle", "ptr", hTimer)
But I dont know how to use SetWaitableTimerEx to get the timer working when needed
With your handle, I do not get any errors, but I get no callback. :think:
Soft wrote:nice to have true sub ms timer, but MicroTimer uses too much cpu than native ahk's settimer.
can't handle the timer with games.. :(
Try this, not sub-Ms, but still far more accurate than settimer and low CPU.
Cheers.
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

08 Apr 2017, 01:27

Try this, not sub-Ms, but still far more accurate than settimer and low CPU.
Cheers.
Thanks, I tried, but WaitableTimer is not working well with my codes unlike MicroTimer or SetTimer.
Code gets stuck when new menu subroutine interrupts? I need to figure out what's wrong now.. :eh:
AutoHotkey & AutoHotkey_H v1.1.22.07
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

08 Apr 2017, 07:40

@Soft: So what, you want CreateMicro from MicroTimer, but you need less CPU usage?
I did see a post on the thread of the code that I used saying you could reduce CPU usage with a slight change, want me to build you a version with that change in?
Edit: DLL Attached.

The original author thinks this will sacrifice accuracy for lower CPU usage. As I asked in the thread though, surely if you delay a timer by the same amount of time every tick, it is still accurate (Apart, maybe, from the 1st tick?).
Please test and let me know how it compares.
Attachments
MicroTimer.zip
MicroTimer with Patel's mod
(4.25 KiB) Downloaded 192 times
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

08 Apr 2017, 19:00

The original author thinks this will sacrifice accuracy for lower CPU usage. As I asked in the thread though, surely if you delay a timer by the same amount of time every tick, it is still accurate (Apart, maybe, from the 1st tick?).
Please test and let me know how it compares.
yeah I wanted to use MicroTimer, thanks for the updated dll
cpu usuage is significantly lower than the one without a change, (was getting almost 20% sometimes, but now getting 0.7% cpu usage, same code)
and still more accurate compared to ahk's native settimer!
AutoHotkey & AutoHotkey_H v1.1.22.07
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

09 Apr 2017, 21:12

evilC wrote:OK, well this change is minimal - I could add it as an optional mode or something?

Would this tweak only really be needed for the microsecond timers?
tweaking only microtimer would be enough. Besides, I don't understand the need for WaitableTimer.
AutoHotkey & AutoHotkey_H v1.1.22.07
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

13 Apr 2017, 19:35

I was impressed by the smoothness of the mouse movement using MicroTimer, but the cursor got often stuck when quickly switching directions.
For me this is the best ratio smoothness vs CPU-usage:

Code: Select all

; 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.
ntchs:=2
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.

~Right::
loop
{
	DllCall("user32.dll\mouse_event", "UInt", 0x0001, "UInt", 1*ntchs, "UInt", 0, "UInt", 0, "UPtr", 0)
    DllCall("Sleep", UInt, SleepDuration)  ; Must use DllCall instead of the Sleep command.
	if !GetKeyState("Right", "P")
	break
}
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
return
~Left::
loop
{
	DllCall("user32.dll\mouse_event", "UInt", 0x0001, "UInt", -1*ntchs, "UInt", 0, "UInt", 0, "UPtr", 0)
    DllCall("Sleep", UInt, SleepDuration)  ; Must use DllCall instead of the Sleep command.
	if !GetKeyState("Left", "P")
	break
}
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
return
~Down::
loop
{
	DllCall("user32.dll\mouse_event", "UInt", 0x0001, "UInt", 0, "UInt", 1*ntchs, "UInt", 0, "UPtr", 0)
    DllCall("Sleep", UInt, SleepDuration)  ; Must use DllCall instead of the Sleep command.
	if !GetKeyState("Down", "P")
	break
}
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
return
~Up::
loop
{
	DllCall("user32.dll\mouse_event", "UInt", 0x0001, "UInt", 0, "UInt", -1*ntchs, "UInt", 0, "UPtr", 0)
    DllCall("Sleep", UInt, SleepDuration)  ; Must use DllCall instead of the Sleep command.
	if !GetKeyState("Up", "P")
	break
}
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
return


^Esc::
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
ExitApp
return

arcticir
Posts: 694
Joined: 17 Nov 2013, 11:32

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

18 Apr 2017, 01:49

It is too complicated to call, and can have a more simple way to use it?
E.g: DllCall("MicroTimer.dll\Sleep", "UInt",1)
thanks.
Trigun
Posts: 41
Joined: 29 Mar 2017, 01:33

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

21 May 2017, 11:01

i don't understand ... with this code i can run a sleep that trigger when i want and not on tick timer?
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

23 May 2017, 16:26

arcticir wrote:It is too complicated to call, and can have a more simple way to use it?
E.g: DllCall("MicroTimer.dll\Sleep", "UInt",1)
thanks.
DllCall("MicroTimer.dll\Sleep", "UInt",1) is C syntax, this code is C#
I guess it would be possible, but I don't do C at the moment.

It may be possible to implement it as an equivalent to Sleep, rather than as an equivalent to SetTimer (Which would simplify the syntax considerably) - if/when I get time, I will look into it.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

23 May 2017, 16:28

Trigun wrote:i don't understand ... with this code i can run a sleep that trigger when i want and not on tick timer?
AHK's SetTimer and Sleep commands do not work properly with values less than ~10-15 (Not AHK's fault, just a limitation of the Windows APIs it uses)

This library provides a replacement for SetTimer that is (reasonably) accurate to values <15
It even goes <1 (Into the nanosecond range)

Working around these techniques in pure AHK is possible, but it is not as CPU efficient as doing it in compiled C# code (And seeing as we could be talking about code running REALLY often, this can become a huge issue), hence this library.
Trigun
Posts: 41
Joined: 29 Mar 2017, 01:33

Re: MicroTimer - Sub-10ms timers for AHK (C# DLL)

23 Jun 2017, 02:56

i don't understand the code... (the demo don't do anything)
if i want replace my sleep commands with this library how i can do it?
something like

Code: Select all

 sleep (t) {
DllCall("MicroTimer.dll\Sleep", "UInt",t)
}

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 119 guests