How to optimize the speed of a script as much as possible.

Helpful script writing tricks and HowTo's
User avatar
WAZAAAAA
Posts: 67
Joined: 13 Jan 2015, 19:48

How to optimize the speed of a script as much as possible.

14 Feb 2015, 14:00

This code disables several ON-by-default "obscure" delays, unnecessary debug functions, gives an higher CPU priority to your scripts and contains an high precision Sleep function.
Please note that inside the spoiler of note #8 you can find a good benchmark script to test the speed of your scripts.


Generic optimizations
Just copy this at the beginning of any AHK script to make use of the performance optimizations:

Code: [Select all] [Expand] [Download] (OPTIMIZE.AHK)GeSHi © Codebox Plus




^Notes
1. #NoEnv is recommended for all scripts, it disables environment variables.
Spoiler
2. The default #MaxHotkeysPerInterval along with #HotkeyInterval will stop your script by showing message boxes if you have some kind of rapid autofire loop in it. Just put some insane unreachable high number to ignore this limit.
Spoiler
3. ListLines and #KeyHistory are functions used to "log your keys". Disable them as they're only useful for debugging purposes.
Spoiler
4. Setting an higher priority to a Windows program is supposed to improve its performance. Use AboveNormal/A. If you feel like it's making things worse, comment or remove this line.
Spoiler
5. The default SetBatchLines value makes your script sleep 10 milliseconds every line. Make it -1 to not sleep (but remember to include at least one Sleep in your loops, if any!)
Spoiler
6. Even though SendInput ignores SetKeyDelay, SetMouseDelay and SetDefaultMouseSpeed, having these delays at -1 improves SendEvent's speed just in case SendInput is not available and falls back to SendEvent.
Spoiler
7. SetWinDelay and SetControlDelay may affect performance depending on the script.
Spoiler
8. SendInput is the fastest send method. SendEvent (the default one) is 2nd place, SendPlay a far 3rd place (it's the most compatible one though). SendInput does not obey to SetKeyDelay, SetMouseDelay, SetDefaultMouseSpeed; there is no delay between keystrokes in that mode.
Spoiler
9. If you're not using any SetTimer, the high precision sleep function is useful when you need millisecond reliability in your scripts. It may be problematic when used with SetTimer in some situations because this sleep method pauses the entire script. To make use of it, here's an example that waits 16,67 milliseconds:
DllCall("Sleep",UInt,16.67)
Spoiler
10. When using PixelSearch to scan a single pixel of a single color variation, don't use the Fast parameter. According to my benchmarks, regular PixelSearch is faster than PixelSearch Fast in that case.
Spoiler
11. According to the documentation (this text is found in the setup file), the Unicode x64bit version of AHK is faster, use it when available.
Spoiler




Defining variables (benchmark)
explained by jNizM
jNizM wrote:Speed Test (Benchmark): Define Variables

explained by dd900
dd900 wrote:
Documentation wrote:Performance: In v1.0.48+, the comma operator is usually faster than writing separate expressions, especially when assigning one variable to another (e.g. x:=y, a:=b). Performance continues to improve as more and more expressions are combined into a single expression; for example, it may be 35% faster to combine five or ten simple expressions into a single expression.


As suggested by the documentation I have changed

Code: [Select all] [Download] GeSHi © Codebox Plus

a := 1
b := 2
c := 3
d := Function( param )
e := AnotherFunc( d )
return
to

Code: [Select all] [Download] GeSHi © Codebox Plus

a := 1
, b := 2
, c := 3
, d := Function( param )
, e := AnotherFunc( d )
return


My project is rather large and doing this over my entire script has caused a noticeable increase in speed.




VarSetCapacity optimization
Sam_ wrote:in my experience (and according to the documentation), using VarSetCapacity() improves performance when building a string by means of gradual concatenation. From the documentation:

In addition to its uses described at DllCall, this function can also be used to enhance performance when building a string by means of gradual concatenation. This is because multiple automatic resizings can be avoided when you have some idea of what the string's final length will be.


From the example in the documentation (although not a working example...):

Code: [Select all] [Download] GeSHi © Codebox Plus

; Optimize by ensuring MyVar has plenty of space to work with.
VarSetCapacity(MyVar, 10240000) ; ~10 MB
Loop
{
...
MyVar = %MyVar%%StringToConcatenate%
...
}




Speedup DllCall
jNizM wrote:Speedup DllCall's (excluded: "User32.dll", "Kernel32.dll", "ComCtl32.dll" & "Gdi32.dll")

Found in old Forum (Code by Bentschi)

Functions: LoadLibrary() & FreeLibrary()

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus



Test Script:

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus



Result:

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus



Export Table:

Code: [Select all] [Download] GeSHi © Codebox Plus

global gdiplus := LoadLibrary("gdiplus")
for name, proc in gdiplus
{
if (name!="_ptr" && name!="_ref" && name!="__delete")
MsgBox % name
}




Ternary vs if/else
jNizM wrote:

Code: [Select all] [Download] GeSHi © Codebox Plus

Ternarry:        2.828439
if/else: 3.931492

Code:
Spoiler




Don't spread math/variables over multiple lines
SvenBent wrote:Avoid spreading math over multiple lines and using variable for intermediate results if they are only used once.
As much as possible condense math into one line and only use variables for storing math results if you need the results being used multiple times later.

remember that:
1x calc < 1x calc + 1x memory read < 2x calc



Code: [Select all] [Expand] [Download] (Untitled.ahk)GeSHi © Codebox Plus



The second condensed method is slightly more than 50% faster




Gosub VS functions
SvenBent wrote:This is probably way down into miniscule area but
Using Gosub seems to be 25% faster than using a function.




SetProcessWorkingSetSize VS EmptyWorkingSet
SvenBent wrote:Another miniscule speed tip

Replacing

Code: [Select all] [Download] (Untitled.ahk)GeSHi © Codebox Plus

    DllCall("SetProcessWorkingSetSize", Int,-1, Int,-1, Int,-1 )

with

Code: [Select all] [Download] (Untitled.ahk)GeSHi © Codebox Plus

    return, dllcall("psapi.dll\EmptyWorkingSet", "UInt", -1)

You get around a 27% speedup on that emptymem call




Checking Boolean values with if
SvenBent wrote:a few tips for IF checking of Boolean values
Tested on a Core2Quad Q6600 system.


if VariableName
Seems to be the fastest way to check if a variable is True


if VariableName = 0
Is the fastest way to check if a variable is false however it does not take into account of the variable is not set, aka empty. The IF commands does not get activaged if the variable is not set/empty

if VariableName <> 1
is almost as fast and an empty variable is considere false ( aka the IF settings get activated) just like if it contained a 0

if Not VariableName
Seems to be slower than both of the two above






Previous inputs from the members of the archived forum are located here: https://autohotkey.com/board/topic/9445 ... -possible/
Feel free to add any possible contribution and I will include them. GOTTA GO FAST.
Last edited by WAZAAAAA on 04 Nov 2016, 12:50, edited 22 times in total.
YOU'RE NOT ALEXANDER
User avatar
WAZAAAAA
Posts: 67
Joined: 13 Jan 2015, 19:48

Re: How to optimize the speed of a script as much as possibl

14 Feb 2015, 14:08

Oh my, maybe I should've posted this in the Tutorials section. Moderators what do you think?
YOU'RE NOT ALEXANDER
User avatar
joedf
Posts: 5833
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: How to optimize the speed of a script as much as possibl

14 Feb 2015, 15:26

I think it's fine here. ;)
Skrell
Posts: 163
Joined: 23 Jan 2014, 12:05

Re: How to optimize the speed of a script as much as possibl

18 Feb 2015, 13:15

I REALLY like where this thread is heading! I've always wanted to know more about speed optimizations in AHK! Please post more benchmarks/comparisons if possible on ANY functions in AHK that essentially do the same thing. For instance, i use a LOT of settimer commands in my code and would love to know if you have any thoughts on it.
Also can you please comment more on these values (what the trade off is in using them):

SetTitleMatchMode 2
SetTitleMatchMode fast
SetBatchLines, -1
SetKeyDelay, -1, -1, -1
SetMouseDelay, -1
SetWinDelay, -1
SetControlDelay, -1
SetDefaultMouseSpeed, 0
User avatar
jNizM
Posts: 2283
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: How to optimize the speed of a script as much as possibl

19 Feb 2015, 01:39

Speed Test (Benchmark): Define Variables
[AHK] 1.1.26.01 x64 Unicode | [WIN] 10 Pro (Version 1703) x64
My GitHub Profile | Donations are appreciated if I could help you
Skrell
Posts: 163
Joined: 23 Jan 2014, 12:05

Re: How to optimize the speed of a script as much as possibl

19 Feb 2015, 08:47

jNizM wrote:Speed Test (Benchmark): Define Variables

Would you mind explaining what this is doing?
QPC(R := 0)
{
static P := 0, F := 0, Q := DllCall("QueryPerformanceFrequency", "Int64P", F)
return ! DllCall("QueryPerformanceCounter", "Int64P", Q) + (R ? (P := Q) / F : (Q - P) / F)
}

I dont understand how this function manages to measure the time it takes to assign variables by using QPC(0) and QPC(1) :(
User avatar
jNizM
Posts: 2283
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: How to optimize the speed of a script as much as possibl

19 Feb 2015, 08:56

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus

[AHK] 1.1.26.01 x64 Unicode | [WIN] 10 Pro (Version 1703) x64
My GitHub Profile | Donations are appreciated if I could help you
Ffkwa

Re: How to optimize the speed of a script as much as possible.

05 Jun 2015, 01:13

Ok, I can't edit. So here are my findings:

1. Realtime priority is bad news almost always. Don't do that. It almost never works, even if your script is written correctly. If your script is written incorrectly, then you can cause your entire system to hang.
2. High priority/above normal priority sometimes work. You can try them, but they are a very big potential source of problems. Disable them if something is going wrong.
3. Spintest sleep (sleepz) doesn't work when scripting for programs that require CPU time to process. For example, if you're trying to script for a game, then your script will hog all the processor time and the game will lag.
4. Batchlines 0 forces your script to consume CPU time even when you sleep. That absolutely kills performance in the rest of the system. Don't do it.
5. Setting batchlines to normal right before Sleep seems to not change anything at all. It still consumes huge CPU timeslices.
6. WAZAA is absolutely correct about the speed of SendMode Input and SendMode Event, but the difference is tiny compared to the variability of Sleep. I suspect that Event may still be less reliable, but I am not sure and don't have meaningful tests for reliability.
lexikos
Posts: 5410
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: How to optimize the speed of a script as much as possible.

05 Jun 2015, 03:18

Batchlines 0 forces your script to consume CPU time even when you sleep.
No. SetBatchLines does not affect Sleep at all. It determines how often the program "rests" (sleeps for 10ms) in between execution of lines of script. If the script is not executing code (i.e. it is idle, waiting or sleeping), the setting has no effect.

That absolutely kills performance in the rest of the system. Don't do it.
On my 5yo system, a single script utilizing maximum CPU time does not noticeably affect performance at all, let alone "kill" it. Most systems have at least a dual-core CPU, and a script is only capable of tying up one core.

It still consumes huge CPU timeslices.
There's something wrong with your script, or your system. While sleeping, the program checks its message queue and then yields its CPU time slice, and repeats. There is generally no measurable CPU usage unless there is a flood of messages to process (having been sent to the script's own window(s)).



Edit:
To be clear, I am not condoning the OP's suggestions. For instance, these will not make the script run faster, and might even be undesirable:
Most scripts will never need more than the default #MaxThreads (10). That means something like a hotkey starts, then is interrupted by another, and another, etc. so that there are 10 running that have not yet completed. If that happens and you hit the default limit, there's probably a problem with the design of your script.

#MaxMem: "The purpose of limiting each variable's capacity is to prevent a buggy script from consuming all available system memory. Raising or lowering the limit does not affect the performance of a script"

The default settings for #MaxHotkeysPerInterval and #HotkeyInterval enable a safe-guard against accidentally causing infinite loops with hotkeys that send keys which can trigger hotkeys. Using the settings shown above would disable this safe-guard. (However, disabling it is sometimes useful - for example, some Logitech mice have a free-spinning mouse wheel, which can easily exceed the default hotkey limit for a WheelUp/WheelDown hotkey.)
Ffkwa

Re: How to optimize the speed of a script as much as possible.

05 Jun 2015, 09:13

I'm glad I posted. Thanks for the informative response!

lexikos wrote:
Batchlines 0 forces your script to consume CPU time even when you sleep.
No. SetBatchLines does not affect Sleep at all. It determines how often the program "rests" (sleeps for 10ms) in between execution of lines of script. If the script is not executing code (i.e. it is idle, waiting or sleeping), the setting has no effect.

That absolutely kills performance in the rest of the system. Don't do it.
On my 5yo system, a single script utilizing maximum CPU time does not noticeably affect performance at all, let alone "kill" it. Most systems have at least a dual-core CPU, and a script is only capable of tying up one core.

This was my mistake. This is affected by my test setup I forgot to mention. I'm on VMWare, with only one core. So anything that behaves badly causes system-wide lockdown for me.

It still consumes huge CPU timeslices.
There's something wrong with your script, or your system. While sleeping, the program checks its message queue and then yields its CPU time slice, and repeats. There is generally no measurable CPU usage unless there is a flood of messages to process (having been sent to the script's own window(s)).

You are right, my testing script had a mistake. I was testing a special sleep() function which used normal Sleep for a long duration, then a spintest Sleepz() to have fine resolution at the end. But my function calculated used "Sleep % %period% - 15.7" which meant I was just testing the high resolution timer.
Ffkwa

Re: How to optimize the speed of a script as much as possible.

05 Jun 2015, 09:22

The OP and I are actually both scripting for the same game. The context is that frames take ~16ms - 22ms, and it's important that sent keys resolve on discrete frames.

I'm thinking about letting my script force Windows's built in tick from 15.6 ms to maybe 5 ms. The downside would be power consumption, and probably excessive context switches, but maybe it will improve Sleep consistency. I'm also kind of wondering how VMWare will handle this tick resolution, given that I've disabled its "backdoor" port that lets it communicate with the host system. Would my host system get the changed tick as well? And if my script crashes and forgets to reset the timer, will my system be stuck at a 5ms tick until reset?
Guest

Re: How to optimize the speed of a script as much as possible.

05 Jun 2015, 10:00

Doing some reading, I found that the timer tick rate will reset when your process terminates, which is good.
User avatar
KuroiLight
Posts: 197
Joined: 12 Apr 2015, 20:24
GitHub: KuroiLight

Re: How to optimize the speed of a script as much as possible.

06 Jun 2015, 12:13

SetBatchLines, -1 should be enough, as long as you sleep somewhere in your loops.

That absolutely kills performance in the rest of the system. Don't do it.
On my 5yo system, a single script utilizing maximum CPU time does not noticeably affect performance at all, let alone "kill" it. Most systems have at least a dual-core CPU, and a script is only capable of tying up one core.

Whether or not it kills performance really depends on the system and script, but I'd still recommend against anything non-critical running as real-time (possibly starving the process your scripting for); if you need that much of the cpus time for a script it might be better to revisit the code to see if it can be reduced.

edit*:
The default settings for #MaxHotkeysPerInterval and #HotkeyInterval enable a safe-guard against accidentally causing infinite loops with hotkeys that send keys which can trigger hotkeys. Using the settings shown above would disable this safe-guard. (However, disabling it is sometimes useful - for example, some Logitech mice have a free-spinning mouse wheel, which can easily exceed the default hotkey limit for a WheelUp/WheelDown hotkey.)

It would be nice to be able to turn of the popup you get when these limits are exceeded, and just halt the script for a time. :shifty:


just my 2cents.
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
lexikos
Posts: 5410
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: How to optimize the speed of a script as much as possible.

06 Jun 2015, 19:04

KuroiLight wrote:It would be nice to be able to turn of the popup you get when these limits are exceeded, and just halt the script for a time.
On the surface that seems like a good idea, but the point of the limit and the popup is that hitting the limit often indicates an error in the script which should be fixed, and the popup directs users to the solution. Suppressing the popup and instead blocking the hotkey for a time would likely result in the script being left as is, not behaving the way the user expects - either because they get an occasional flood of hotkeys (from a send-hotkey loop) that wasn't intentional, or because their rapid-fire hotkey doesn't work consistently.

If you're specifically opting to suppress the popup but keeping the limit, that basically means you expect the hotkey to fire very rapidly sometimes, but still want to limit the number of activations per interval. I don't think that would be a common case.
User avatar
KuroiLight
Posts: 197
Joined: 12 Apr 2015, 20:24
GitHub: KuroiLight

Re: How to optimize the speed of a script as much as possible.

07 Jun 2015, 19:20

lexikos wrote:
KuroiLight wrote:It would be nice to be able to turn of the popup you get when these limits are exceeded, and just halt the script for a time.
On the surface that seems like a good idea, but the point of the limit and the popup is that hitting the limit often indicates an error in the script which should be fixed, and the popup directs users to the solution. Suppressing the popup and instead blocking the hotkey for a time would likely result in the script being left as is, not behaving the way the user expects - either because they get an occasional flood of hotkeys (from a send-hotkey loop) that wasn't intentional, or because their rapid-fire hotkey doesn't work consistently.

If you're specifically opting to suppress the popup but keeping the limit, that basically means you expect the hotkey to fire very rapidly sometimes, but still want to limit the number of activations per interval. I don't think that would be a common case.

I agree, I've only encountered this once so far with my scroll script, since someones mouse wheel could potentially send 10s or 100s of events at a time. I think it would be better if after a set limit of it would end the thread, e.g.

Code: [Select all] [Download] GeSHi © Codebox Plus

#MaxThreadsPerHotkey, 1
#InterruptMaxThread, true

allowing only 1 thread for the hotkey to exist at a time and interrupting it if its too slow, but not blocking it. ofc this has problems too.

sorry to derail the thread op, i'm done.
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
lexikos
Posts: 5410
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: How to optimize the speed of a script as much as possible.

07 Jun 2015, 23:31

I don't understand your suggestion

One of three things can happen if the hotkey is activated when it already has a thread running:
  • The new thread interrupts the older ones, which can be resumed some time after the new thread finishes.
  • The hotkey is running the maximum number of threads, so the event is ignored/discarded (#MaxThreadsBuffer Off).
  • The hotkey is running the maximum number of threads, so the event is buffered/postponed (#MaxThreadsBuffer On).
Maybe what you're saying is that one of the hotkey's previous threads should be terminated before starting the new thread, but that simply can't be done with the current thread design. It also has nothing to do with the problem #MaxHotkeysPerInterval is meant to detect: rapidly repeated (not simultaneous) activations of a hotkey.
User avatar
KuroiLight
Posts: 197
Joined: 12 Apr 2015, 20:24
GitHub: KuroiLight

Re: How to optimize the speed of a script as much as possible.

08 Jun 2015, 01:52

lexikos wrote:hotkey's previous threads should be terminated before starting the new thread,

yeah, this is what I meant, no big deal though, I can work around it.
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
Sam_
Posts: 40
Joined: 20 Mar 2014, 20:24

Re: How to optimize the speed of a script as much as possible.

22 Jun 2015, 16:50

So, taking the above into account, following the recommendations in the documentation (generally a wise move), and rearranging a bit, we now have:

Code: [Select all] [Expand] [Download] GeSHi © Codebox Plus



Edited SetKeyDelay. See next post.
Last edited by Sam_ on 22 Jun 2015, 20:27, edited 2 times in total.
lexikos
Posts: 5410
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: How to optimize the speed of a script as much as possible.

22 Jun 2015, 19:16

SetKeyDelay, -1, -1, -1
The third parameter should be the word "Play" or nothing at all. -1 has no meaning.
Sam_
Posts: 40
Joined: 20 Mar 2014, 20:24

Re: How to optimize the speed of a script as much as possible.

22 Jun 2015, 20:29

lexikos wrote:
SetKeyDelay, -1, -1, -1
The third parameter should be the word "Play" or nothing at all. -1 has no meaning.
Thanks for the catch! I updated my last post.

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 4 guests