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
#NoEnv
#MaxHotkeysPerInterval 99000000
#HotkeyInterval 99000000
#KeyHistory 0
ListLines Off
Process, Priority, , A
SetBatchLines, -1
SetKeyDelay, -1, -1
SetMouseDelay, -1
SetDefaultMouseSpeed, 0
SetWinDelay, -1
SetControlDelay, -1
SendMode Input
;YOUR SCRIPT GOES HERE, but first some recommendations
;the Unicode x64bit version is the fastest AHK installation
;use PixelSearch without Fast if you're searching for a single pixel of a single shade
DllCall("Sleep",UInt,16.67) ;I just used the precise sleep function to wait exactly 16,67 milliseconds!
^Notes
1. #NoEnv is recommended for all scripts, it disables environment variables.
DllCall("Sleep",UInt,16.67)
Defining variables (benchmark)
explained by jNizM
explained by dd900jNizM wrote:Speed Test (Benchmark): Define Variables
dd900 wrote:As suggested by the documentation I have changedDocumentation 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.toCode: Select all
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.Code: Select all
a := 1 , b := 2 , c := 3 , d := Function( param ) , e := AnotherFunc( d ) return
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:
From the example in the documentation (although not a working example...):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.Code: Select all
; 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()Test Script:Code: Select all
LoadLibrary(filename) { static ref := {} if (!(ptr := p := DllCall("LoadLibrary", "str", filename, "ptr"))) return 0 ref[ptr,"count"] := (ref[ptr]) ? ref[ptr,"count"]+1 : 1 p += NumGet(p+0, 0x3c, "int")+24 o := {_ptr:ptr, __delete:func("FreeLibrary"), _ref:ref[ptr]} if (NumGet(p+0, (A_PtrSize=4) ? 92 : 108, "uint")<1 || (ts := NumGet(p+0, (A_PtrSize=4) ? 96 : 112, "uint")+ptr)=ptr || (te := NumGet(p+0, (A_PtrSize=4) ? 100 : 116, "uint")+ts)=ts) return o n := ptr+NumGet(ts+0, 32, "uint") loop % NumGet(ts+0, 24, "uint") { if (p := NumGet(n+0, (A_Index-1)*4, "uint")) { o[f := StrGet(ptr+p, "cp0")] := DllCall("GetProcAddress", "ptr", ptr, "astr", f, "ptr") if (Substr(f, 0)==((A_IsUnicode) ? "W" : "A")) o[Substr(f, 1, -1)] := o[f] } } return o } FreeLibrary(lib) { if (lib._ref.count>=1) lib._ref.count -= 1 if (lib._ref.count<1) DllCall("FreeLibrary", "ptr", lib._ptr) }
Result:Code: Select all
loops := 1000000 SetBatchLines, -1 global gdiplus := LoadLibrary("gdiplus") VarSetCapacity(bin, 20, 0) NumPut(1, bin, 0, "int") DllCall(gdiplus.GdiplusStartup, "ptr*", token, "ptr", &bin, "ptr", 0) DllCall(gdiplus.GdipCreateBitmapFromScan0, "int", 1, "int", 1, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap) start := A_TickCount loop % loops DllCall("gdiplus\GdipBitmapGetPixel", "ptr", pBitmap, "int", 1, "int", 1, "uint*", col) timeA := A_TickCount-start start := A_TickCount loop % loops DllCall(gdiplus.GdipBitmapGetPixel, "ptr", pBitmap, "int", 1, "int", 1, "uint*", col) timeB := A_TickCount-start DllCall(gdiplus.DisposeImage, "ptr", pBitmap) DllCall(gdiplus.Cleanup, "ptr", token) MsgBox % "Normal:`n" timeA "`n`nWith LoadLibrary:`n" timeB "`n`n" timeA/timeB
Export Table:Code: Select all
--------------------------- LoadLibrary.ahk --------------------------- Normal: 1828 With LoadLibrary: 781 2.340589 --------------------------- OK ---------------------------
Code: Select all
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:Code: Select all
Ternarry: 2.828439 if/else: 3.931492
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
The second condensed method is slightly more than 50% fasterCode: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. SendMode Input ; Recommended for new scripts due to its superior speed and reliability. SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. #Singleinstance force SetBatchlines, -1 ListLines Off #KeyHistory 0 var_Input1:=123 var_Input2:=456 start:=A_tickcount Loop 99999999 { X:= (2 * var_Input1 ) -1 Y:= (3 / var_Input2 ) +7 Z:= X / Y } Results1:=A_tickcount - start start:=A_tickcount Loop 99999999 { Z:= ((2 * var_Input1 ) -1) / ((3 / var_Input2 ) +7) } Results2:= A_tickcount - start msgbox %Results1% - %Results2%
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
ReplacingwithCode: Select all
DllCall("SetProcessWorkingSetSize", Int,-1, Int,-1, Int,-1 )
You get around a 27% speedup on that emptymem callCode: Select all
return, dllcall("psapi.dll\EmptyWorkingSet", "UInt", -1)
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.