GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

Post your working scripts, libraries and tools.
sashaatx
Posts: 354
Joined: 27 May 2021, 08:27
Contact:

GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

10 Jan 2024, 13:21

https://github.com/samfisherirl/Compare-Code-Speed-GUI-AHKv2-Optimize-Scripts-For-Speed
Screenshot 2024-03-03 193302.png
Screenshot 2024-03-03 193302.png (43.28 KiB) Viewed 1316 times
Credits:

original function viewtopic.php?f=7&t=6413
WAZAAAAA viewtopic.php?f=7&t=6413
g33k for richcode https://github.com/G33kDude/RichCode.ahk

Code: Select all

/*
MIT License

Copyright (c) 2017 GeekDude

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/
#Requires Autohotkey v2
#SingleInstance Force
; credit to g33k for https://github.com/G33kDude/RichCode.ahk

; constants
logDir := A_MyDocuments "\ahk_log\"
if !FileExist(logDir)
	DirCreate(logDir)
temp := logDir "code_to_run.ahk", logger := logDir "log.txt", userLog := logDir "userlog.txt", ahk := A_AhkPath, title := "AHKv2 Quick Speed Test"

tester := constructGUI()
try load()


constructGUI()
{
	global tester, loops, code1, code2, code3
	w := 550
	h := 550
	tester := {}
	tester := Gui()
	
	darkmode(tester)
	tester.SetFont("s15 cWhite", "Consolas")
	tester.BackColor := "232b2b"
	T := tester.Add("Tab3", "x20 y8 w" w - 40 " h" h - (h / 3), ["Code 1", "Code 2", "Code 3"])
	tabH := h - (h / 3) - 55
	tester.ctrls := {}
	T.UseTab(1)
	
	code1 := RichCode(tester, settings(), "x32 y48 w" w - 65 " h" tabH " r13 +Wrap")
	T.UseTab(2)
	code2 := RichCode(tester, settings(), "x32 y48 w" w - 65 " h" tabH " +Wrap")
	T.UseTab(3)
	code3 := RichCode(tester, settings(), "x32 y48 w" w - 65 " h" tabH " +Wrap")
	T.UseTab()
	code1.SetFont("s11")
	code2.SetFont("s11")
	code3.SetFont("s11")
	resultsY := Round(16 + (h - Round(h / 3)))
	resultsw := Round((w - (w / 3))) - 20
	tester.Add("GroupBox", "x20 y" resultsY " w" w - 40 " h222", "Results")
	runbtn := tester.Add("Button", "x44 y" resultsY + 30 " ", "Run it")
	clear := tester.Add("Button", "x+15 y" resultsY + 30 " ", "Clear")
	logread := tester.Add("Button", "x+15 y" resultsY + 30 " ", "Log")
	tester.Add("Text", "x+15 y" resultsY + 35 "", "Loops x")
	loops := tester.Add("Edit", "x+1 y" resultsY + 35 " w100", "50")
	resultsY += 90
	tester.Add("Text", "x44 y" resultsY " w120 h23 +0x200", "Result 1")
	results1 := tester.Add("Edit", "x+5 y" resultsY " w" resultsw " ", "")
	tester.Add("Text", "x44 y+11 w120 h23 +0x200", "Result 2")
	results2 := tester.Add("Edit", "x+5 yp w" resultsw "  ", "")
	tester.Add("Text", "x44 y+11 w120 h23 +0x200", "Result 3")
	results3 := tester.Add("Edit", "x+5 yp w" resultsw "  ", "")

	tester.OnEvent('Close', (*) => ExitApp())
	runbtn.OnEvent('Click', runbtn_click)
	logread.OnEvent('Click', log_click)
	clear.OnEvent('Click', clear_click)
	tester.Title := title
	darkmode(tester)
		
	rc := RichCode(tester, settings(), "xm w640 h470")
	tester.Show("w" w " h" h + 70)
		

	runbtn_click(*)
	{
		global logDir, temp, userLog
		FileOpen(Logger, "w").Write("&&&&&&" code1.Text "&&&&&&"
			. code2.Text "&&&&&&" code3.Text
			. "&&&&&&" loops.Value "&&&&&&")
		FileOpen(temp, "w").Write(FileContents(code1.Text, code2.Text, code3.Text, Round(loops.Value / 2)))
		FileOpen(logDir "results.txt", "w").Write("")
		Run(ahk ' "' temp '"',,,&PID)
		while (Format("{}", FileOpen(logDir "results.txt", "r").Read()) = "")
			if !ProcessExist(PID)
				return
			else 
				Sleep(100)
		contents := FileOpen(logDir "results.txt", "r").Read()
		results := StrSplit(contents, "&&&&&&")
		{
			if results.Has(1)
				results1.Value := results[1]
			if results.Has(2)
				results2.Value := results[2]
			if results.Has(3)
				results3.Value := results[3]
		}
		if FileExist(userLog)
		{
			contents := FileOpen(userLog, "r").Read()
		} else {
			contents := ""
		}

		FileOpen(userLog, "w").Write("`n@@@@@@@@@@@@@@@@@@@@@@"
			. "`n"   A_MM "-" A_DD "-" A_YYYY " " A_Hour ":" A_Min ":" A_Sec "`n" code1.Text "`nresults: " results1.Value "`n`n"
			. code2.Text "`nresults: " results2.Value "`n" code3.Text
			. "`nresults: " results3.Value "`nloops:" loops.Value "`n" contents)
		try {
			FileDelete(logDir "results.txt")
			FileDelete(temp)
		} catch as e {
			FileOpen(logDir "results.txt", "w").Write("")
		}
	}
	log_click(*)
	{
		if FileExist(userLog)
			Run(userLog)
	}
	clear_click(*)
	{
		global code1, code2, code3
		code1.Text := "", code2.Text := "", code3.Text := ""
	}

	FileContents(c1, c2, c3, loops)
	{
		txt := ""
		txt .= "
(
#SingleInstance Force
#Requires Autohotkey v2
r1 := "", r2 := "", r3 := ""
QPC(1)
f1()
test1 := QPC(0)
QPC(1)

)"
		txt .= (c2 = "") ? "`n"
			: " `nf2()`ntest2 := QPC(0)`nQPC(1)"
		txt .= (c3 = "") ? "`n"
			: " `nf3()`ntest3 := QPC(0)`nQPC(1)`nf3()`ntest4 := QPC(0)`nQPC(1)`n"
		txt .= (c2 = "") ? ""
			: "
(
f2()
test5 := QPC(0)`nQPC(1)
)"
		txt .= "
(

f1()
test6 := QPC(0)
r1 := (test6+test1)/2`n
)"
		txt .= (c2 = "") ? "`n" : "r2:= (test2+test5)/2`n"
		txt .= (c3 = "") ? "`n" : "r3 := (test3+test4)/2`n"
		txt .= "
(
out()
ExitApp()

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) 
}
f1()
{

	
)" "Loop " loops "`n{`n" c1 "`n}`n" "`n}`n"
		txt .= (c2 = "") ? "`n" : "`nf2()`n{`n" "Loop " loops "`n{`n" c2 "`n}`n" "`n}`n"
		txt .= (c3 = "") ? "`n" : "f3()`n{`n" "Loop " loops "`n{`n" c3 "`n}`n" "`n}`n"
		txt .= "
(
	out()
	{
	FileOpen(A_ScriptDir "\results.txt", "w").Write(r1 '&&&&&&' r2 '&&&&&&' r3)`n
	}
	
)"
		return txt
	}
	return tester
}

load()
{
	global tester, logger, loops
	if !FileExist(logger)
		return
	contents := FileOpen(Logger, "r").Read()
	results := StrSplit(contents, "&&&&&&")
	{
		if results.Has(2)
			code1.Text := results[2]
		if results.Has(3)
			code2.Text := results[3]
		if results.Has(4)
			code3.Text := results[4]
		if results.Has(5)
			loops.Value := (results[5] > 5000) ? 5000 : results[5]
	}
	if FileExist(temp)
		try {
			FileDelete(temp)
		} catch as e
		{
			Msgbox e.message
		}
}
blackGuiCtrl(params*)
{
	if params[1].gui.backcolor
	{
		bg := params[1].gui.backcolor
	} else {
		bg := "12121a"
	}
	for ctrl in params
	{
		try
		{
			ctrl.Opt("Background" bg)
		} catch as e {
			continue
		}
	}
}
settings(){
	; Settings array for the RichCode control
	return settings := {
		TabSize: 4,
		Indent: "`t",
		FGColor: 0xEDEDCD,
		BGColor: 0x3F3F3F,
		Font: {Typeface: "Consolas", Size: 11, Bold: false},
		WordWrap: False,
		
		UseHighlighter: True,
		Highlighter: HighlightAHK,
		HighlightDelay: 200,
		Colors: {
			Comments:     0x7F9F7F,
			Functions:    0x7CC8CF,
			Keywords:     0xE4EDED,
			Multiline:    0x7F9F7F,
			Numbers:      0xF79B57,
			Punctuation:  0x97C0EB,
			Strings:      0xCC9893,
			
			; AHK
			A_Builtins:   0xF79B57,
			Commands:     0xCDBFA3,
			Directives:   0x7CC8CF,
			Flow:         0xE4EDED,
			KeyNames:     0xCB8DD9,
			
			; CSS
			ColorCodes:   0x7CC8CF,
			Properties:   0xCDBFA3,
			Selectors:    0xE4EDED,
			
			; HTML
			Attributes:   0x7CC8CF,
			Entities:     0xF79B57,
			Tags:         0xCDBFA3,
			
			; JS
			Builtins:     0xE4EDED,
			Constants:    0xF79B57,
			Declarations: 0xCDBFA3
		}
	}
}


darkMode(myGUI, color?)
{
	if (VerCompare(A_OSVersion, "10.0.17763") >= 0)
	{
		DWMWA_USE_IMMERSIVE_DARK_MODE := 19
		if (VerCompare(A_OSVersion, "10.0.18985") >= 0)
		{
			DWMWA_USE_IMMERSIVE_DARK_MODE := 20
		}
		DllCall("dwmapi\DwmSetWindowAttribute", "Ptr", myGUI.hWnd, "Int", DWMWA_USE_IMMERSIVE_DARK_MODE, "Int*", true, "Int", 4)
		; listView => SetExplorerTheme(LV1.hWnd, "DarkMode_Explorer"), SetExplorerTheme(LV2.hWnd, "DarkMode_Explorer")
		uxtheme := DllCall("GetModuleHandle", "Str", "uxtheme", "Ptr")
		DllCall(DllCall("GetProcAddress", "Ptr", uxtheme, "Ptr", 135, "Ptr"), "Int", 2) ; ForceDark
		DllCall(DllCall("GetProcAddress", "Ptr", uxtheme, "Ptr", 136, "Ptr"))
	}
	for ctrlHWND, ctrl in myGUI
	{
		if ctrl.HasOwnProp("NoBack")
			continue
		blackGuiCtrl(myGUI[ctrlHWND])
	}

}

/*
	class RichCode({"TabSize": 4     ; Width of a tab in characters
		, "Indent": "`t"             ; What text to insert on indent
		, "FGColor": 0xRRGGBB        ; Foreground (text) color
		, "BGColor": 0xRRGGBB        ; Background color
		, "Font"                     ; Font to use
		: {"Typeface": "Courier New" ; Name of the typeface
			, "Size": 12             ; Font size in points
			, "Bold": False}         ; Bolded (True/False)


		; Whether to use the highlighter, or leave it as plain text
		, "UseHighlighter": True

		; Delay after typing before the highlighter is run
		, "HighlightDelay": 200

		; The highlighter function (FuncObj or name)
		; to generate the highlighted RTF. It will be passed
		; two parameters, the first being this settings array
		; and the second being the code to be highlighted
		, "Highlighter": Func("HighlightAHK")

		; The colors to be used by the highlighter function.
		; This is currently used only by the highlighter, not at all by the
		; RichCode class. As such, the RGB ordering is by convention only.
		; You can add as many colors to this array as you want.
		, "Colors"
		: [0xRRGGBB
			, 0xRRGGBB
			, 0xRRGGBB,
			, 0xRRGGBB]})
		*/
Settimer getFocus, 100

getFocus(){
	global code1, code2, code3, focus
	try fc := ControlGetFocus("ahk_id" A_ScriptHwnd)
	if IsSet(fc) && (fc = code1.hwnd || fc != code2.hwnd || fc || code3.hwnd)
		focus := true
	else
		focus := false
}

#HotIf focus = true
Tab::
{
	Loop 3
	{
		SendInput '{Tab}'
	}
}


class RichCode
{
	#DllLoad "msftedit.dll"
	static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
	static MenuItems := ["Cut", "Copy", "Paste", "Delete", "", "Select All", ""
		, "UPPERCASE", "lowercase", "TitleCase"]

	_Frozen := False

	/** @type {Gui.Custom} the underlying control */
	_control := {}

	Settings := {}

	gutter := { Hwnd: 0 }

	; --- Static Methods ---

	static BGRFromRGB(RGB) => RGB >> 16 & 0xFF | RGB & 0xFF00 | RGB << 16 & 0xFF0000

	; --- Properties ---

	Text {
		get => StrReplace(this._control.Text, "`r")
		set => (this.Highlight(Value), Value)
	}

	; TODO: reserve and reuse memory
	selection[i := 0] {
		get => (
			this.SendMsg(0x434, 0, charrange := Buffer(8)), ; EM_EXGETSEL
			out := [NumGet(charrange, 0, "Int"), NumGet(charrange, 4, "Int")],
			i ? out[i] : out
		)

		set => (
			i ? (t := this.selection, t[i] := Value, Value := t) : "",
			NumPut("Int", Value[1], "Int", Value[2], charrange := Buffer(8)),
			this.SendMsg(0x437, 0, charrange), ; EM_EXSETSEL
			Value
		)
	}

	SelectedText {
		get {
			Selection := this.selection
			length := selection[2] - selection[1]
			b := Buffer((length + 1) * 2)
			if this.SendMsg(0x43E, 0, b) > length ; EM_GETSELTEXT
				throw Error("Text larger than selection! Buffer overflow!")
			text := StrGet(b, length, "UTF-16")
			return StrReplace(text, "`r", "`n")
		}

		set {
			this.SendMsg(0xC2, 1, StrPtr(Value)) ; EM_REPLACESEL
			this.Selection[1] -= StrLen(Value)
			return Value
		}
	}

	EventMask {
		get => this._EventMask

		set {
			this._EventMask := Value
			this.SendMsg(0x445, 0, Value) ; EM_SETEVENTMASK
			return Value
		}
	}

	_UndoSuspended := false
	UndoSuspended {
		get {
			return this._UndoSuspended
		}

		set {
			try { ; ITextDocument is not implemented in WINE
				if Value
					this.ITextDocument.Undo(-9999995) ; tomSuspend
				else
					this.ITextDocument.Undo(-9999994) ; tomResume
			}
			return this._UndoSuspended := !!Value
		}
	}

	Frozen {
		get => this._Frozen

		set {
			if (Value && !this._Frozen)
			{
				try ; ITextDocument is not implemented in WINE
					this.ITextDocument.Freeze()
				catch
					this._control.Opt "-Redraw"
			}
			else if (!Value && this._Frozen)
			{
				try ; ITextDocument is not implemented in WINE
					this.ITextDocument.Unfreeze()
				catch
					this._control.Opt "+Redraw"
			}
			return this._Frozen := !!Value
		}
	}

	Modified {
		get {
			return this.SendMsg(0xB8, 0, 0) ; EM_GETMODIFY
		}

		set {
			this.SendMsg(0xB9, Value, 0) ; EM_SETMODIFY
			return Value
		}
	}

	; --- Construction, Destruction, Meta-Functions ---

	__New(gui, Settings, Options := "")
	{
		this.__Set := this.___Set
		this.Settings := Settings
		FGColor := RichCode.BGRFromRGB(Settings.FGColor)
		BGColor := RichCode.BGRFromRGB(Settings.BGColor)

		this._control := gui.AddCustom("ClassRichEdit50W +0x5031b1c4 +E0x20000 " Options)

		; Enable WordWrap in RichEdit control ("WordWrap" : true)
		if this.Settings.HasOwnProp("WordWrap")
			this.SendMsg(0x448, 0, 0)

		; Register for WM_COMMAND and WM_NOTIFY events
		; NOTE: this prevents garbage collection of
		; the class until the control is destroyed
		this.EventMask := 1 ; ENM_CHANGE
		this._control.OnCommand 0x300, this.CtrlChanged.Bind(this)

		; Set background color
		this.SendMsg(0x443, 0, BGColor) ; EM_SETBKGNDCOLOR

		; Set character format
		f := settings.font
		cf2 := Buffer(116, 0)
		NumPut("UInt", 116, cf2, 0)          ; cbSize      = sizeof(CF2)
		NumPut("UInt", 0xE << 28, cf2, 4)    ; dwMask      = CFM_COLOR|CFM_FACE|CFM_SIZE
		NumPut("UInt", f.Size * 20, cf2, 12) ; yHeight     = twips
		NumPut("UInt", fgColor, cf2, 20) ; crTextColor = 0xBBGGRR
		StrPut(f.Typeface, cf2.Ptr + 26, 32, "UTF-16") ; szFaceName = TCHAR
		SendMessage(0x444, 0, cf2, this.Hwnd) ; EM_SETCHARFORMAT

		; Set tab size to 4 for non-highlighted code
		tabStops := Buffer(4)
		NumPut("UInt", Settings.TabSize * 4, tabStops)
		this.SendMsg(0x0CB, 1, tabStops) ; EM_SETTABSTOPS

		; Change text limit from 32,767 to max
		this.SendMsg(0x435, 0, -1) ; EM_EXLIMITTEXT

		; Bind for keyboard events
		; Use a pointer to prevent reference loop
		this.OnMessageBound := this.OnMessage.Bind(this)
		OnMessage(0x100, this.OnMessageBound) ; WM_KEYDOWN
		OnMessage(0x205, this.OnMessageBound) ; WM_RBUTTONUP

		; Bind the highlighter
		this.HighlightBound := this.Highlight.Bind(this)

		; Create the right click menu
		this.menu := Menu()
		for Index, Entry in RichCode.MenuItems
			(entry == "") ? this.menu.Add() : this.menu.Add(Entry, (*) => this.RightClickMenu.Bind(this))

		; Get the ITextDocument object
		bufpIRichEditOle := Buffer(A_PtrSize, 0)
		this.SendMsg(0x43C, 0, bufpIRichEditOle) ; EM_GETOLEINTERFACE
		this.pIRichEditOle := NumGet(bufpIRichEditOle, "UPtr")
		this.IRichEditOle := ComValue(9, this.pIRichEditOle, 1)
		; ObjAddRef(this.pIRichEditOle)
		this.pITextDocument := ComObjQuery(this.IRichEditOle, RichCode.IID_ITextDocument)
		this.ITextDocument := ComValue(9, this.pITextDocument, 1)
		; ObjAddRef(this.pITextDocument)
	}

	RightClickMenu(ItemName, ItemPos, MenuName)
	{
		if (ItemName == "Cut")
			Clipboard := this.SelectedText, this.SelectedText := ""
		else if (ItemName == "Copy")
			Clipboard := this.SelectedText
		else if (ItemName == "Paste")
			this.SelectedText := A_Clipboard
		else if (ItemName == "Delete")
			this.SelectedText := ""
		else if (ItemName == "Select All")
			this.Selection := [0, -1]
		else if (ItemName == "UPPERCASE")
			this.SelectedText := Format("{:U}", this.SelectedText)
		else if (ItemName == "lowercase")
			this.SelectedText := Format("{:L}", this.SelectedText)
		else if (ItemName == "TitleCase")
			this.SelectedText := Format("{:T}", this.SelectedText)
	}

	__Delete()
	{
		; Release the ITextDocument object
		this.ITextDocument := unset, ObjRelease(this.pITextDocument)
		this.IRichEditOle := unset, ObjRelease(this.pIRichEditOle)

		; Release the OnMessage handlers
		OnMessage(0x100, this.OnMessageBound, 0) ; WM_KEYDOWN
		OnMessage(0x205, this.OnMessageBound, 0) ; WM_RBUTTONUP

		; Destroy the right click menu
		this.menu := unset
	}

	__Call(Name, Params) => this._control.%Name%(Params*)
	__Get(Name, Params) => this._control.%Name%[Params*]
	___Set(Name, Params, Value) {
		try {
			this._control.%Name%[Params*] := Value
		} catch Any as e {
			e2 := Error(, -1)
			e.What := e2.What
			e.Line := e2.Line
			e.File := e2.File
			throw e
		}
	}

	; --- Event Handlers ---

	OnMessage(wParam, lParam, Msg, hWnd)
	{
		if (hWnd != this._control.hWnd)
			return

		if (Msg == 0x100) ; WM_KEYDOWN
		{
			if (wParam == GetKeyVK("Tab"))
			{
				; Indentation
				Selection := this.Selection
				if GetKeyState("Shift")
					this.IndentSelection(True) ; Reverse
				else if (Selection[2] - Selection[1]) ; Something is selected
					this.IndentSelection()
				else
				{
					; TODO: Trim to size needed to reach next TabSize
					this.SelectedText := this.Settings.Indent
					this.Selection[1] := this.Selection[2] ; Place cursor after
				}
				return False
			}
			else if (wParam == GetKeyVK("Escape")) ; Normally closes the window
				return False
			else if (wParam == GetKeyVK("v") && GetKeyState("Ctrl"))
			{
				this.SelectedText := A_Clipboard ; Strips formatting
				this.Selection[1] := this.Selection[2] ; Place cursor after
				return False
			}
		}
		else if (Msg == 0x205) ; WM_RBUTTONUP
		{
			this.menu.Show()
			return False
		}
	}

	CtrlChanged(control)
	{
		; Delay until the user is finished changing the document
		SetTimer this.HighlightBound, -Abs(this.Settings.HighlightDelay)
	}

	; --- Methods ---

	; First parameter is taken as a replacement Value
	; Variadic form is used to detect when a parameter is given,
	; regardless of content
	Highlight(NewVal := unset)
	{
		if !(this.Settings.UseHighlighter && this.Settings.Highlighter) {
			if IsSet(NewVal)
				this._control.Text := NewVal
			return
		}

		; Freeze the control while it is being modified, stop change event
		; generation, suspend the undo buffer, buffer any input events
		PrevFrozen := this.Frozen, this.Frozen := True
		PrevEventMask := this.EventMask, this.EventMask := 0 ; ENM_NONE
		PrevUndoSuspended := this.UndoSuspended, this.UndoSuspended := True
		PrevCritical := Critical(1000)

		; Run the highlighter
		Highlighter := this.Settings.Highlighter
		if !IsSet(NewVal)
			NewVal := this.text
		RTF := Highlighter(this.Settings, &NewVal)

		; "TRichEdit suspend/resume undo function"
		; https://stackoverflow.com/a/21206620


		; Save the rich text to a UTF-8 buffer
		buf := Buffer(StrPut(RTF, "UTF-8"))
		StrPut(RTF, buf, "UTF-8")

		; Set up the necessary structs
		zoom := Buffer(8, 0) ; Zoom Level
		point := Buffer(8, 0) ; Scroll Pos
		charrange := Buffer(8, 0) ; Selection
		settextex := Buffer(8, 0) ; SetText settings
		NumPut("UInt", 1, settextex) ; flags = ST_KEEPUNDO

		; Save the scroll and cursor positions, update the text,
		; then restore the scroll and cursor positions
		MODIFY := this.SendMsg(0xB8, 0, 0)    ; EM_GETMODIFY
		this.SendMsg(0x4E0, ZOOM.ptr, ZOOM.ptr + 4)   ; EM_GETZOOM
		this.SendMsg(0x4DD, 0, POINT)        ; EM_GETSCROLLPOS
		this.SendMsg(0x434, 0, CHARRANGE)    ; EM_EXGETSEL
		this.SendMsg(0x461, SETTEXTEX, Buf) ; EM_SETTEXTEX
		this.SendMsg(0x437, 0, CHARRANGE)    ; EM_EXSETSEL
		this.SendMsg(0x4DE, 0, POINT)        ; EM_SETSCROLLPOS
		this.SendMsg(0x4E1, NumGet(ZOOM, "UInt")
			, NumGet(ZOOM, 4, "UInt"))        ; EM_SETZOOM
		this.SendMsg(0xB9, MODIFY, 0)         ; EM_SETMODIFY

		; Restore previous settings
		Critical PrevCritical
		this.UndoSuspended := PrevUndoSuspended
		this.EventMask := PrevEventMask
		this.Frozen := PrevFrozen
	}

	IndentSelection(Reverse := False, Indent := unset) {
		; Freeze the control while it is being modified, stop change event
		; generation, buffer any input events
		PrevFrozen := this.Frozen
		this.Frozen := True
		PrevEventMask := this.EventMask
		this.EventMask := 0 ; ENM_NONE
		PrevCritical := Critical(1000)

		if !IsSet(Indent)
			Indent := this.Settings.Indent
		IndentLen := StrLen(Indent)

		; Select back to the start of the first line
		sel := this.selection
		top := this.SendMsg(0x436, 0, sel[1]) ; EM_EXLINEFROMCHAR
		bottom := this.SendMsg(0x436, 0, sel[2]) ; EM_EXLINEFROMCHAR
		this.Selection := [
			this.SendMsg(0xBB, top, 0), ; EM_LINEINDEX
			this.SendMsg(0xBB, bottom + 1, 0) - 1 ; EM_LINEINDEX
		]

		; TODO: Insert newlines using SetSel/ReplaceSel to avoid having to call
		; the highlighter again
		Text := this.SelectedText
		out := ""
		if Reverse { ; Remove indentation appropriately
			loop parse text, "`n", "`r" {
				if InStr(A_LoopField, Indent) == 1
					Out .= "`n" SubStr(A_LoopField, 1 + IndentLen)
				else
					Out .= "`n" A_LoopField
			}
		} else { ; Add indentation appropriately
			loop parse Text, "`n", "`r"
				Out .= "`n" Indent . A_LoopField
		}
		this.SelectedText := SubStr(Out, 2)

		this.Highlight()

		; Restore previous settings
		Critical PrevCritical
		this.EventMask := PrevEventMask

		; When content changes cause the horizontal scrollbar to disappear,
		; unfreezing causes the scrollbar to jump. To solve this, jump back
		; after unfreezing. This will cause a flicker when that edge case
		; occurs, but it's better than the alternative.
		point := Buffer(8, 0)
		this.SendMsg(0x4DD, 0, POINT) ; EM_GETSCROLLPOS
		this.Frozen := PrevFrozen
		this.SendMsg(0x4DE, 0, POINT) ; EM_SETSCROLLPOS
	}

	; --- Helper/Convenience Methods ---

	SendMsg(Msg, wParam, lParam) =>
		SendMessage(msg, wParam, lParam, this._control.Hwnd)
}


class HighlightAHK {
	static flow := "if|else|loop|loop files|loop parse|loop read|loop reg|while|for|continue|break|until|try|throw|"
		. "catch|finally|class|global|local|static|return|goto"
	static library := (
		"Abs|ACos|ASin|ATan|BlockInput|Break|Buffer|CallbackCreate|CallbackFree|CaretGetPos|Catch|Ceil|Chr|Click|"
		"ClipboardAll|ClipWait|ComCall|ComObjActive|ComObjArray|ComObjConnect|ComObject|ComObjFlags|ComObjFromPtr|"
		"ComObjGet|ComObjQuery|ComObjType|ComObjValue|ComValue|Continue|ControlAddItem|ControlChooseIndex|"
		"ControlChooseString|ControlClick|ControlDeleteItem|ControlFindItem|ControlFocus|ControlGetChecked|"
		"ControlGetChoice|ControlGetClassNN|ControlGetEnabled|ControlGetFocus|ControlGetHwnd|ControlGetIndex|"
		"ControlGetItems|ControlGetPos|ControlGetStyle|ControlGetText|ControlGetVisible|ControlHide|"
		"ControlHideDropDown|ControlMove|ControlSend|ControlSetChecked|ControlSetEnabled|ControlSetStyle|"
		"ControlSetText|ControlShow|ControlShowDropDown|CoordMode|Cos|Critical|DateAdd|DateDiff|DetectHiddenText|"
		"DetectHiddenWindows|DirCopy|DirCreate|DirDelete|DirExist|DirMove|DirSelect|DllCall|Download|DriveEject|"
		"DriveGetCapacity|DriveGetFileSystem|DriveGetLabel|DriveGetList|DriveGetSerial|DriveGetSpaceFree|"
		"DriveGetStatus|DriveGetStatusCD|DriveGetType|DriveLock|DriveRetract|DriveSetLabel|DriveUnlock|Edit|"
		"EditGetCurrentCol|EditGetCurrentLine|EditGetLine|EditGetLineCount|EditGetSelectedText|EditPaste|Else|EnvGet|"
		"EnvSet|Exit|ExitApp|Exp|FileAppend|FileCopy|FileCreateShortcut|FileDelete|FileEncoding|FileExist|"
		"FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileOpen|FileRead|"
		"FileRecycle|FileRecycleEmpty|FileSelect|FileSetAttrib|FileSetTime|Finally|Float|Floor|For|Format|FormatTime|"
		"GetKeyName|GetKeySC|GetKeyState|GetKeyVK|GetMethod|Goto|GroupActivate|GroupAdd|GroupClose|GroupDeactivate|Gui|"
		"GuiCtrlFromHwnd|GuiFromHwnd|HasBase|HasMethod|HasProp|HotIf|Hotkey|Hotstring|If|IL_Add|IL_Create|IL_Destroy|"
		"ImageSearch|IniDelete|IniRead|IniWrite|InputBox|InputHook|InstallKeybdHook|InstallMouseHook|InStr|Integer|"
		"IsLabel|IsObject|IsSet|KeyHistory|KeyWait|ListHotkeys|ListLines|ListVars|ListViewGetContent|Ln|LoadPicture|"
		"Log|Loop|Map|Max|Menu|MenuBar|MenuFromHandle|MenuSelect|Min|Mod|MonitorGet|MonitorGetCount|MonitorGetName|"
		"MonitorGetPrimary|MonitorGetWorkArea|MouseClick|MouseClickDrag|MouseGetPos|MouseMove|MsgBox|Number|NumGet|"
		"NumPut|ObjAddRef|ObjBindMethod|ObjGetBase|ObjGetCapacity|ObjHasOwnProp|ObjOwnPropCount|ObjOwnProps|ObjSetBase|"
		"ObjSetCapacity|OnClipboardChange|OnError|OnExit|OnMessage|Ord|OutputDebug|Pause|Persistent|PixelGetColor|"
		"PixelSearch|PostMessage|ProcessClose|ProcessExist|ProcessGetName|ProcessGetParent|ProcessGetPath|"
		"ProcessSetPriority|ProcessWait|ProcessWaitClose|Random|RegCreateKey|RegDelete|RegDeleteKey|RegExMatch|"
		"RegExReplace|RegRead|RegWrite|Reload|Return|Round|Run|RunAs|RunWait|Send|SendEvent|SendInput|SendLevel|"
		"SendMessage|SendMode|SendPlay|SendText|SetCapsLockState|SetControlDelay|SetDefaultMouseSpeed|SetKeyDelay|"
		"SetMouseDelay|SetNumLockState|SetRegView|SetScrollLockState|SetStoreCapsLockMode|SetTimer|SetTitleMatchMode|"
		"SetWinDelay|SetWorkingDir|Shutdown|Sin|Sleep|Sort|SoundBeep|SoundGetInterface|SoundGetMute|SoundGetName|"
		"SoundGetVolume|SoundPlay|SoundSetMute|SoundSetVolume|SplitPath|Sqrt|StatusBarGetText|StatusBarWait|StrCompare|"
		"StrGet|String|StrLen|StrLower|StrPtr|StrPut|StrReplace|StrSplit|StrUpper|SubStr|Suspend|Switch|SysGet|"
		"SysGetIPAddresses|Tan|Thread|Throw|ToolTip|TraySetIcon|TrayTip|Trim|Try|Type|Until|VarSetStrCapacity|"
		"VerCompare|While|WinActivate|WinActivateBottom|WinActive|WinClose|WinExist|WinGetClass|WinGetClientPos|"
		"WinGetControls|WinGetControlsHwnd|WinGetCount|WinGetID|WinGetIDLast|WinGetList|WinGetMinMax|WinGetPID|"
		"WinGetPos|WinGetProcessName|WinGetProcessPath|WinGetStyle|WinGetText|WinGetTitle|WinGetTransColor|"
		"WinGetTransparent|WinHide|WinKill|WinMaximize|WinMinimize|WinMinimizeAll|WinMove|WinMoveBottom|WinMoveTop|"
		"WinRedraw|WinRestore|WinSetAlwaysOnTop|WinSetEnabled|WinSetRegion|WinSetStyle|WinSetTitle|WinSetTransColor|"
		"WinSetTransparent|WinShow|WinWait|WinWaitActive|WinWaitClose"
	)
	static keynames := (
		"alt|altdown|altup|appskey|backspace|blind|browser_back|browser_favorites|browser_forward|browser_home|"
		"browser_refresh|browser_search|browser_stop|bs|capslock|click|control|ctrl|ctrlbreak|ctrldown|ctrlup|del|"
		"delete|down|end|enter|esc|escape|f1|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f2|f20|f21|f22|f23|f24|f3|f4|f5|"
		"f6|f7|f8|f9|home|ins|insert|joy1|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy2|joy20|joy21|"
		"joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy3|joy30|joy31|joy32|joy4|joy5|joy6|joy7|joy8|joy9|joyaxes|"
		"joybuttons|joyinfo|joyname|joypov|joyr|joyu|joyv|joyx|joyy|joyz|lalt|launch_app1|launch_app2|launch_mail|"
		"launch_media|lbutton|lcontrol|lctrl|left|lshift|lwin|lwindown|lwinup|mbutton|media_next|media_play_pause|"
		"media_prev|media_stop|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|"
		"numpadadd|numpadclear|numpaddel|numpaddiv|numpaddot|numpaddown|numpadend|numpadenter|numpadhome|numpadins|"
		"numpadleft|numpadmult|numpadpgdn|numpadpgup|numpadright|numpadsub|numpadup|pause|pgdn|pgup|printscreen|ralt|"
		"raw|rbutton|rcontrol|rctrl|right|rshift|rwin|rwindown|rwinup|scrolllock|shift|shiftdown|shiftup|space|tab|up|"
		"volume_down|volume_mute|volume_up|wheeldown|wheelleft|wheelright|wheelup|xbutton1|xbutton2"
	)
	static builtins := "A_\w+|true|false|this|super"
	static needle := (
		"ims)"
		"((?:^|\s);[^\n]+)"          ; Comments
		"|(^\s*/\*.*?(?:^\s*\*\/|\*/\s*$|\z))"    ; Multiline comments
		"|(^\s*#\w+\b(?!:)(?:(?<!HotIf)[^\n]*)?)" ; Directives
		"|([$#+*!~&/\\<>^|=?:,().``%}{\[\]\-]+)"   ; Punctuation
		"|\b(0x[0-9a-fA-F]+|[0-9]+)" ; Numbers
		"|('[^'\n]*'|" . '"[^"\n]*")' ; Strings
		"|\b(" this.builtins ")\b"  ; A_Builtins
		"|\b(" this.flow ")\b"            ; Flow
		"|\b(" this.library ")(?!\()\b"       ; Commands
		"|\b(" this.keynames ")\b"        ; Keynames
		; "|\b(" this.keywords ")\b"        ; Other keywords
		"|(\w+(?=\())"     ; Functions
	)

	static Call(Settings, &Code) {
		GenHighlighterCache(Settings)
		Map := Settings.Cache.ColorMap

		rtf := ""
		Pos := 1
		while FoundPos := RegExMatch(Code, this.needle, &Match, Pos) {
			RTF .= (
				"\cf" Map.Plain " "
				EscapeRTF(SubStr(Code, Pos, FoundPos - Pos))
				"\cf" (
					Match.1 != "" && Map.Comments ||
					Match.2 != "" && Map.Multiline ||
					Match.3 != "" && Map.Directives ||
					Match.4 != "" && Map.Punctuation ||
					Match.5 != "" && Map.Numbers ||
					Match.6 != "" && Map.Strings ||
					Match.7 != "" && Map.A_Builtins ||
					Match.8 != "" && Map.Flow ||
					Match.9 != "" && Map.Commands ||
					Match.10 != "" && Map.Keynames ||
					; Match.11 != "" && Map.Keywords ||
					Match.11 != "" && Map.Functions ||
					Map.Plain
				) " "
				EscapeRTF(Match.0)
			), Pos := FoundPos + Match.Len
		}

		return Settings.Cache.RTFHeader . RTF
			. "\cf" Map.Plain " " EscapeRTF(SubStr(Code, Pos)) "\`n}"
	}
}
GenHighlighterCache(Settings)
{
	if Settings.HasOwnProp("Cache")
		return
	Cache := Settings.Cache := {}
	
	
	; --- Process Colors ---
	Cache.Colors := Settings.Colors.Clone()
	
	; Inherit from the Settings array's base
	BaseSettings := Settings
	while (BaseSettings := BaseSettings.Base)
		if BaseSettings.HasProp("Colors")
			for Name, Color in BaseSettings.Colors.OwnProps()
				if !Cache.Colors.HasProp(Name)
					Cache.Colors.%Name% := Color
	
	; Include the color of plain text
	if !Cache.Colors.HasOwnProp("Plain")
		Cache.Colors.Plain := Settings.FGColor
	
	; Create a Name->Index map of the colors
	Cache.ColorMap := {}
	for Name, Color in Cache.Colors.OwnProps()
		Cache.ColorMap.%Name% := A_Index
	
	
	; --- Generate the RTF headers ---
	RTF := "{\urtf"
	
	; Color Table
	RTF .= "{\colortbl;"
	for Name, Color in Cache.Colors.OwnProps()
	{
		RTF .= "\red"   Color>>16 & 0xFF
		RTF .= "\green" Color>>8  & 0xFF
		RTF .= "\blue"  Color     & 0xFF ";"
	}
	RTF .= "}"
	
	; Font Table
	if Settings.Font
	{
		FontTable .= "{\fonttbl{\f0\fmodern\fcharset0 "
		FontTable .= Settings.Font.Typeface
		FontTable .= ";}}"
		RTF .= "\fs" Settings.Font.Size * 2 ; Font size (half-points)
		if Settings.Font.Bold
			RTF .= "\b"
	}
	
	; Tab size (twips)
	RTF .= "\deftab" GetCharWidthTwips(Settings.Font) * Settings.TabSize
	
	Cache.RTFHeader := RTF
}

GetCharWidthTwips(Font)
{
	static Cache := Map()
	
	if Cache.Has(Font.Typeface "_" Font.Size "_" Font.Bold)
		return Cache[Font.Typeface "_" font.Size "_" Font.Bold]
	
	; Calculate parameters of CreateFont
	Height := -Round(Font.Size*A_ScreenDPI/72)
	Weight := 400+300*(!!Font.Bold)
	Face := Font.Typeface
	
	; Get the width of "x"
	hDC := DllCall("GetDC", "UPtr", 0)
	hFont := DllCall("CreateFont"
	, "Int", Height ; _In_ int     nHeight,
	, "Int", 0      ; _In_ int     nWidth,
	, "Int", 0      ; _In_ int     nEscapement,
	, "Int", 0      ; _In_ int     nOrientation,
	, "Int", Weight ; _In_ int     fnWeight,
	, "UInt", 0     ; _In_ DWORD   fdwItalic,
	, "UInt", 0     ; _In_ DWORD   fdwUnderline,
	, "UInt", 0     ; _In_ DWORD   fdwStrikeOut,
	, "UInt", 0     ; _In_ DWORD   fdwCharSet, (ANSI_CHARSET)
	, "UInt", 0     ; _In_ DWORD   fdwOutputPrecision, (OUT_DEFAULT_PRECIS)
	, "UInt", 0     ; _In_ DWORD   fdwClipPrecision, (CLIP_DEFAULT_PRECIS)
	, "UInt", 0     ; _In_ DWORD   fdwQuality, (DEFAULT_QUALITY)
	, "UInt", 0     ; _In_ DWORD   fdwPitchAndFamily, (FF_DONTCARE|DEFAULT_PITCH)
	, "Str", Face   ; _In_ LPCTSTR lpszFace
	, "UPtr")
	hObj := DllCall("SelectObject", "UPtr", hDC, "UPtr", hFont, "UPtr")
	size := Buffer(8, 0)
	DllCall("GetTextExtentPoint32", "UPtr", hDC, "Str", "x", "Int", 1, "Ptr", SIZE)
	DllCall("SelectObject", "UPtr", hDC, "UPtr", hObj, "UPtr")
	DllCall("DeleteObject", "UPtr", hFont)
	DllCall("ReleaseDC", "UPtr", 0, "UPtr", hDC)
	
	; Convert to twpis
	Twips := Round(NumGet(size, 0, "UInt")*1440/A_ScreenDPI)
	Cache[Font.Typeface "_" Font.Size "_" Font.Bold] := Twips
	return Twips
}

EscapeRTF(Code)
{
	for Char in ["\", "{", "}", "`n"]
		Code := StrReplace(Code, Char, "\" Char)
	return StrReplace(StrReplace(Code, "`t", "\tab "), "`r")
}
Last edited by sashaatx on 08 Mar 2024, 22:43, edited 6 times in total.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
sashaatx
Posts: 354
Joined: 27 May 2021, 08:27
Contact:

Re: AHKv2 Test Multiple Scripts for Speed Optimization

03 Mar 2024, 19:35

updated to include richcode, and some surprising findings.

Commas and Combined lines aren't faster in v2; with caveats.
============================================================


Over the course of 3 months, I've learned some optimization tricks. After a deeper dive and some reflection, I've found I had some misconceptions on the transient ability of shortcuts held over from AHKv1. I noticed a lot of this syntax in experienced community members' scripts, including mine, and I want to share what I've found.

- If you have tricks or tips for *faster* code in ahkv2, please share. Always excited to learn and share.
- All notes will come from this post regarding Optimizing ahkv1. viewtopic.php?f=7&t=6413
- If you want to replicate the tests, they can be found here
- v1) viewtopic.php?f=7&t=6413
- v2   https://github.com/samfisherirl/Compare-Code-Speed-GUI-AHKv2-Optimize-Scripts-For-Speed/tree/main/_speedTestScripts
- None of this post describes results outside the bounds of the tests. There are use cases that are faster in v2 with combined lines, just a bit of hyperbole in the title.
- My tool for testing is DLLCall query counter
- all functions get run 55000 times, and a few reruns for insurance.

https://github.com/samfisherirl/Compare-Code-Speed-GUI-AHKv2-Optimize-Scripts-For-Speed

The tests

Defining variables by lines and commas

tests are shortened for brevity

Code: Select all

;test 1
t1a := 1
t1b := 1
t1c := 1
t1d := 1

;test 2
 t2f := t2g := t2h := t2i := t2j := 1

;test3
t3a := 1, t3b := 1, t3c := 1, t3d := 1


AHKv1 results =

Code: Select all

 	;test1 0.240315

	;test2 0.132753

	;test3 0.168953
ahkv2 results =

Code: Select all

	 ;test1 0.00124844 (50% + faster)

	;test2 0.00259254

	;test3 0.00274485


We can see combining variables on a single line in these examples are no longer faster but hamper the code. We'll find out this is different with function calls. Let's do it again with functions.

Code: Select all

 
 ; these functions are across all tests ; condensed
	e() {   y := 999*222
	   return y }

	f() {  y := 999*222
	   return y }
	g() {   y := 999*222
	   return y }
test1

Code: Select all

```
	a := e()
	b := f()
	c := g()

```
test2

Code: Select all

```
a := e(),b := f(),c := g()

```
test3

Code: Select all

```
    a := e()
,b := f()
,c := g()

```
results

Code: Select all

```
	;test1 0.01627 (50% slower)
	;test2 0.01098
	;test3 0.011008

```
Even shortened conditionals aren't faster with combined lines

;test1

Code: Select all

```
x := true

if x
   z:=1, a:=2, b:=1, c:=2

```
;test2

Code: Select all

```
	x := true

	if x
	{
	   z:=1
	   a:=2
	   b:=1
	   c:=2
	}

```
- test1 0.0026

- test2 0.00180 ;30% faster
Attachments
Screenshot 2024-03-03 193302.png
Screenshot 2024-03-03 193302.png (43.28 KiB) Viewed 1315 times
Last edited by sashaatx on 08 Mar 2024, 22:47, edited 9 times in total.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
jsong55
Posts: 321
Joined: 30 Mar 2021, 22:02

Re: GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

04 Mar 2024, 19:30

Great finding. Any idea why combining in single line is slightly slower? How about if else vs ternary and switch cases
sashaatx
Posts: 354
Joined: 27 May 2021, 08:27
Contact:

Re: GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

04 Mar 2024, 23:27

jsong55 wrote:
04 Mar 2024, 19:30
Great finding. Any idea why combining in single line is slightly slower? How about if else vs ternary and switch cases
I would only be speculating. From reading when I first discovered this, as an interpreter for a language becomes more efficient, deviations from that designed path can be inhibitive to the efficiency. This may be 100% incorrect, take with a grain of salt, I dont even write c++.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
geek
Posts: 1068
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: AHKv2 Test Multiple Scripts for Speed Optimization

06 Mar 2024, 21:18

sashaatx wrote:
03 Mar 2024, 19:35
updated to include richcode, and some surprising findings.
When you go to modify and re-share code developed by other people, please pay attention to how their code is licensed and make sure to follow the license terms. RichCode is licensed under the MIT license, which does allow redistribution with modification, but requires that
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
which you have not done here. Can you please update your original post in line with the license terms?
sashaatx
Posts: 354
Joined: 27 May 2021, 08:27
Contact:

Re: AHKv2 Test Multiple Scripts for Speed Optimization

07 Mar 2024, 17:02

geek wrote:
06 Mar 2024, 21:18
sashaatx wrote:
03 Mar 2024, 19:35
updated to include richcode, and some surprising findings.
When you go to modify and re-share code developed by other people, please pay attention to how their code is licensed and make sure to follow the license terms. RichCode is licensed under the MIT license, which does allow redistribution with modification, but requires that
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
which you have not done here. Can you please update your original post in line with the license terms?
adjustments made
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
eugenesv
Posts: 182
Joined: 21 Dec 2015, 10:11

Re: GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

08 Mar 2024, 08:30

Try this test, runs faster than first one, so commas still win!

Code: Select all

Loop 1000000
  t2a:=t2b:=t2c:=t2d:=t2e:=1
  t2f:=t2g:=t2h:=t2i:=t2j:=1
test2 := QPC(0), QPC(1)

Loop 1000000
  t3a:=1,t3b:=1,t3c:=1,t3d:=1,t3e:=1
  t3f:=1,t3g:=1,t3h:=1,t3i:=1,t3j:=1
test3 := QPC(0), QPC(1)
sashaatx
Posts: 354
Joined: 27 May 2021, 08:27
Contact:

Re: GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

08 Mar 2024, 15:14

eugenesv wrote:
08 Mar 2024, 08:30
Try this test, runs faster than first one, so commas still win!

Code: Select all

Loop 1000000
  t2a:=t2b:=t2c:=t2d:=t2e:=1
  t2f:=t2g:=t2h:=t2i:=t2j:=1
test2 := QPC(0), QPC(1)

Loop 1000000
  t3a:=1,t3b:=1,t3c:=1,t3d:=1,t3e:=1
  t3f:=1,t3g:=1,t3h:=1,t3i:=1,t3j:=1
test3 := QPC(0), QPC(1)
Good catch! But Let me know if I understand, because if Im incorrect I may have bad results.
It appears the way its written you have not included the initial QPC(1)
And I might be seeing a concatenated line because the way it reads for each:

Code: Select all

run 100000 times:
  t3a:=1,t3b:=1,t3c:=1,t3d:=1,t3e:=1
run once: 
  t3f:=1,t3g:=1,t3h:=1,t3i:=1,t3j:=1
I cant find the block section of the docs that discusses bracketless blocks, to check if loops are only relegated to a single line.

here is my rendition:

Code: Select all

#Requires Autohotkey v2
#SingleInstance force

QPC(1)
Loop 1000000
  t2a:=t2b:=t2c:=t2d:=t2e:=t2g:=t2h:=t2i:=t2j:=1
test2 := QPC(0)

QPC(1)
Loop 1000000
  t3a:=1,t3b:=1,t3c:=1,t3d:=1,t3e:=1,t3f:=1,t3g:=1,t3h:=1,t3i:=1
test3 := QPC(0)


FileAppend("`n`n1: " test2 "`n2:" test3, "log.txt", )

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) 
}
heres my results:

Code: Select all


1: 0.1227718
2: 0.1797694

1: 0.23341200000000001
2: 0.36747239999999998

1: 0.21795980000000001
2: 0.32423940000000001
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
eugenesv
Posts: 182
Joined: 21 Dec 2015, 10:11

Re: GUI for Testing Multiple Scripts Over 1ks Of Iterations for Speed Optimization

09 Mar 2024, 00:08

The initial QPC(1) is included, I've just replaced the subparts of your script that I've pasted. But you're correct to notice that I've made another mistake - the second line indeed doesn't run in a loop! Oops...

Interesting thing is that if you combine just a single pair of variables you get slower results! Would be curious to know why that's the case (this is also something I've been using as I've read and tested it before to be faster, but if it isn't - good riddance ;))

Code: Select all

test3 := QPC(0), QPC(1)
Loop 1000000 {
  t4a:=1
  t4b:=1
  t4c:=1
  t4d:=1
  t4e:=1
  t4f:=1
  t4g:=1
  t4h:=1
  t4i:=1,t4j:=1
}
test4 := QPC(0), QPC(1)

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: lone525 and 38 guests