AutoHotkey Script Speed Problems (Scripting Insights)

When Debugging AutoHotkey Script Speed Problems, Look at Loops First

A number of scripting techniques can cause apps to run slowly. These slowdowns might occur when running long loops, doing extensive searches, or making numerous hard drive reads and writes. But the primary culprit tends to hide within loops or redundant operations of any type. However, you may need to look deeper to find the real source of the problem.

Tip: If you like to keep numerous other programs open, then that alone can cause significant slowing as your computer shares processing time. For example, Google Chrome runs an independent process for each browser tab. Multiple Google Chrome tabs can put a heavy load on the CPU and memory causing delays in any other apps. To optimize time tests, minimize interference by closing all other programs.

InstantHotstring Load Time
The script speed problem appeared to be the result of the non-hidden GUI window. Not so!

Dealing with Script Speeds

As discussed in a recent blog about timing the InstantHotstring.ahk script speed, I timed a few possible changes to the script. A number of factors affect script running delays, therefore specific tests vary—although they should come close depending upon the computer and other processes running on the computer. The following test results reflect the loading of the EmojiInsert.ahk file including over 1000 Hotstrings into the InstantHotstring.ahk app:

Technique File First Load File Reload
Original Configuration Showing GUI Window and the Counting Progress Bar 39.969 Seconds 74.516 Seconds
Hides the GUI Window 7.172 Seconds 29.579 Seconds
Hides the GUI Window and Removes the Hotstring Count from Progress Bar 6.953 Seconds 25.875 Seconds
Hidden Window and No Progress Bar 5.641 Seconds 4.453 Seconds

It seemed that the number one factor for increasing load speed involved hiding the GUI window. The time for loading the test file dropped by a factor of six. By comparison, even removing the newly added Progress GUI control offer negligible improvement. However, I reached the wrong conclusion! I did not fully understand the inner workings of this supposed solution.

The Hotstring Update Routine

When adding a new Hotstring to the DropDownList GUI control, AutoHotkey first checks for the existence of the same previously-activated Hotstring. If found, it deletes the original from the DropDownList GUI control and activates the updated version—then adding it back to the DropDownList GUI control. The routine appears as follows:

ControlGet, Items, List,, ComboBox1, A
Loop, Parse, Items, `n
{
  If InStr(A_LoopField, ":" . NewString . "::", CaseSensitive)
  {
    HotString := StrSplit(A_LoopField, ":",,5)
    OldOptions := HotString[2]
    Control, Delete, %A_Index% , ComboBox1
    Break
  }
}

Library BenefitsThis routine uses the ControlGet, OutputVar, List command to copy the contents of the DropDownList GUI control into the variable Items. Since the list potentially changes after every new Hotstring, this step occurs for each added Hotstring.

Next, the Loop searches for a match. If found, the script deletes the old DropDownList GUI control item using the Control Command. The DropDownList GUI control requires this step (per the blog, Create Instant Hotstrings Using the AutoHotkey Hotstring() Function) since, as far as I could see, AutoHotkey offer no update option for items in the DropDownList GUI control.

In my first test, I hid the GUI window. Since the script ran so much faster when I hid the pop-up window, I initially thought that I had solved the slowdown problem caused when adding Hotstrings from a file. I turned out that the above routine (when working) eats up the time (hidden or not).

Control and ControlGet Versus GUIControl and GUIControlGet

The options and actions offered by the Control and ControlGet commands differ from their sister GuiControl and GuiControlGet commands. But one of the biggest differences shows up in the fact that GuiControl type commands can work on hidden windows while the Control type commands can’t. That means that when I hide the main GUI windows, the routine (shown above) just stopped working—thus, speeding up the script by merely adding the Hotstrings rather than checking for duplicates.

When I set DetectHiddenWindows, On to force the Control and ControlGet commands to work, the script slowed down again—almost as much as it did with the original non-hidden GUI. Hiding the GUI merely prevented the above routine from functioning—thereby, speeding up the entire Hotstring loading process.

Unfortunately, since the GuiControl and/or GuiControlGet commands don’t include equivalent actions, I couldn’t replicate the same Control and ControlGet updating action on a hidden window. In the InstantHotstring.ahk script’s current form, I can either run the update routine for the individual Hotstrings in the file and live with the time delay or bypass the routine and load the file Hotstrings “as is” at an increased speed.

What’s the difference? If you load a particular Hotstring file twice with the routine working, each Hotstring loads only once (slowly). Without the DropDownList GUI control update routine, the script loads every Hotstring twice—although at high speed.

Trap to Block the Hotstring Updating Routine When Loading from Files

I created the variable LoadHotstrings which the script sets to 1 whenever loading Hotstrings from a file. This allows the script to bypass the update routine when loading from files:

If (LoadHotstrings != 1)
{
  ControlGet, Items, List,, ComboBox1, A
  Loop, Parse, Items, `n
  {
    If InStr(A_LoopField, ":" . NewString . "::", CaseSensitive)
    {
      HotString := StrSplit(A_LoopField, ":",,5)
      OldOptions := HotString[2]
      Control, Delete, %A_Index% , ComboBox1
      Break
    }
  }
}

If you want the automatic Hotstring checking and updating action when loading from a file (and don’t mind the slower speed), you can comment out or remove the first If () conditional and the last close parenthesis:

; If (LoadHotstrings != 1)
.
.
.
; }

If you don’t plan to load Hotstrings which need updating from a file, then leave the code alone—understanding that you will get duplicates if you load the same file twice.

Another Possible Fix

If you want to take the time, you could probably write a routine which changes the algorithms for the components causing the time lag. An alternative approach might load the entire DropDownList GUI control contents into the Items variable only once. Then, eliminating the Loop, Parse command, use the InStr() function and StrReplace() function to work directly on the variable without updating the DropDownList GUI control until after adding and updating all the new Hotstrings in the Items variable. Finally, after replacing all newlines (`n) with the pipe character (|), overwrite the entire DropDownList GUI control list by replacing the contents of the entire DropDownList GUI control with the Hotstring list variable Items with one GuiControl command by preceding the replacement with the pipe character (|).:

Items := StrReplace(Items,"n​","|")
GuiControl, , ComboBox1 , |%Items%

I might add this approach to the script at a later date.

Click the Follow button at the top of the sidebar on the right of this page for e-mail notification of new blogs. (If you’re reading this on a tablet or your phone, then you must scroll all the way to the end of the blog—pass any comments—to find the Follow button.)

jack

This post was proofread by Grammarly
(Any other mistakes are all mine.)

(Full disclosure: If you sign up for a free Grammarly account, I get 20¢. I use the spelling/grammar checking service all the time, but, then again, I write a lot more than most people. I recommend Grammarly because it works and it’s free.)

2 thoughts on “AutoHotkey Script Speed Problems (Scripting Insights)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s