Hallo,
ich denke, diese Performancediskussion ist im Hilfeforum nicht richtig untergebracht. Und weil sich mir kein besserer Platz aufgedrängt hat, geht es jetzt hier weiter (wenn jemand mit ausreichenden Rechten meint, das gehöre an einen anderen Ort, bitte verschieben).
Nach dem oben verlinkten Beitrag von AspirinJunkie habe ich selbst noch einmal Alles gecheckt. Wie AU3s StringInStr() kann auch AHKs InStr() in drei verschiedenen Modi arbeiten:
- case-sensitive (1)
- case-insensitive (2)
- case-insensitive-locale (0)
Ich habe deshalb die Testskripte so abgeändert, dass sie nacheinander alle drei Modi verwenden.
AHK
#NoEnv
; Die Ermittlung der CPU-Zeit per UDF und DllCall kostet einige Zeit,
; AHK muss deshalb im "Turbomodus" laufen.
SetBatchLines, -1 ; Schaltet den "Turbo" ein
SetFormat, FloatFast, 0.15 ; 15 Nachkommastellen für Floats
QPC() ; QPC initialisieren
RunTime := A_TickCount
Chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
StringSplit, Chars, Chars
Len := StrLen(Chars)
String := "`r`n"
Loop, %Len% {
I := A_Index
Loop, %Len% {
J := A_Index
Loop, %Len% {
K := A_Index
String .= Chars%I% . Chars%J% . Chars%K% . "`r`n"
}
}
}
Find := "ZAB"
Loops := 1000
T1 := 0, T2 := 0, T3 := 0
Loop, %Loops% {
StringCaseSense, Off
QPC()
Index := Instr(String, "`r`n" . Find . "`r`n")
T1 += QPC()
M1 := SubStr(String, Index + 2, StrLen(Find))
QPC()
Index := Instr(String, "`r`n" . Find . "`r`n", 1)
T2 += QPC()
M2 := SubStr(String, Index + 2, StrLen(Find))
StringCaseSense, Locale
QPC()
Index := Instr(String, "`r`n" . Find . "`r`n")
T3 += QPC()
M3 := SubStr(String, Index + 2, StrLen(Find))
}
RunTime := A_TickCount - RunTime
MsgBox, 0, AHK-Ergebnis für InStr():
, % "Durchschnittliche Ausführungszeit bei " . Loops . " Aufrufen:`r`n`r`n"
. "CaseSense Off: " . (T1 / Loops) . " ms - Treffer: " . M1 . "`r`n"
. "CaseSense On : " . (T2 / Loops) . " ms - Treffer: " . M2 . "`r`n"
. "CaseSense Loc: " . (T3 / Loops) . " ms - Treffer: " . M3 . "`r`n`r`n"
. "Gesamtlaufzeit: " . RunTime . " ms"
ExitApp
QPC() {
Static F := 0, L, Q
If !(F) {
DllCall("QueryPerformanceFrequency", "Int64 *", F), F/=1000
Q := DllCall("GetProcAddress", "UInt", DllCall("GetModuleHandle", "Str", "Kernel32"), "Str", "QueryPerformanceCounter")
}
DllCall(Q, "Int64 *", A)
Return (A-L)/F, L:=A
}
Alles anzeigen
AU3
$RunTime = TimerInit()
[/autoit] [autoit][/autoit] [autoit]$Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
$aChars = StringSplit($Chars, "", 2)
$Len = StringLen($Chars)
$String = @CRLF
For $I = 0 To $Len - 1
For $J = 0 To $Len -1
For $K = 0 To $Len - 1
$String &= $aChars[$I] & $aChars[$J] & $aChars[$K] & @CRLF
Next
Next
Next
$Find = "ZAB"
[/autoit] [autoit][/autoit] [autoit]$Loops = 1000
$T1 = 0
$T2 = 0
$T3 = 0
For $C = 1 To $Loops
$T = TimerInit()
$Index = StringInStr($String, @CRLF & $Find & @CRLF, 2)
$T1 += TimerDiff($T)
$M1 = StringMid($String, $Index + 2, StringLen($Find))
$T = TimerInit()
$Index = StringInStr($String, @CRLF & $Find & @CRLF, 1)
$T2 += TimerDiff($T)
$M2 = StringMid($String, $Index + 2, StringLen($Find))
$T = TimerInit()
$Index = StringInStr($String, @CRLF & $Find & @CRLF, 0)
$T3 += TimerDiff($T)
$M3 = StringMid($String, $Index + 2, StringLen($Find))
Next
$RunTime = TimerDiff($RunTime)
MsgBox(0, "AU3-Ergebnis für StringInStr():", _
"Durchschnittliche Ausführungszeit bei " & $Loops & " Aufrufen: " & @CRLF & @CRLF & _
"CaseSense 2: " & ($T1 / $Loops) & " ms - Treffer: " & $M1 & @CRLF & _
"CaseSense 1: " & ($T2 / $Loops) & " ms - Treffer: " & $M2 & @CRLF & _
"CaseSense 0: " & ($T3 / $Loops) & " ms - Treffer: " & $M3 & @CRLF & @CRLF & _
"Gesamtlaufzeit: " & Round($RunTime) & " ms")
Exit
[/autoit]
Damit die Ergebnisse vergleichbarer sind, habe ich die Zeitmessung in AHK in eine UDF verlagert. Als Ausgleich für den dadurch entstehenden Overhead läuft das Skript jetzt ungebremst.
Die Ergebnisse dieser Testreihen sind für mich einigermaßen überraschend, hier folgen zwei tendenziell zutreffende Ergebnisse:
Zitat von AHKAlles anzeigen---------------------------
AHK-Ergebnis für InStr():
---------------------------
Durchschnittliche Ausführungszeit bei 1000 Aufrufen:CaseSense Off: 0.428947278208604 ms - Treffer: ZAB
CaseSense On : 0.196518084128098 ms - Treffer: ZAB
CaseSense Loc: 2.989166362033329 ms - Treffer: ZABGesamtlaufzeit: 3672 ms
---------------------------
OK
---------------------------
Zitat von AU3Alles anzeigen---------------------------
AU3-Ergebnis für StringInStr():
---------------------------
Durchschnittliche Ausführungszeit bei 1000 Aufrufen:CaseSense 2: 9.80909455594318 ms - Treffer: ZAB
CaseSense 1: 0.896266052027835 ms - Treffer: ZAB
CaseSense 0: 6.51779494937525 ms - Treffer: ZABGesamtlaufzeit: 17343 ms
---------------------------
OK
---------------------------
Dass der case-sensitive Vergleich in beiden Sprachen der schnellste ist, wundert mich nicht. Dass allerdings der Abstand zwischen case-insensitive und case-insensitive-locale in AHK deutlich größer ausfällt, hätte ich nicht erwartet. Dass in AU3 case-insensitive mehr Zeit benötigt als case-insensitive-locale, ist für mich aber überhaupt nicht nachvollziehbar. In der AHK-Hilfe steht zum Thema case-insensitive-locale: "(Locale is 1 to 8 times slower than Off depending on the nature of the strings being compared)". Vielleicht gibt es stärkere Schwankungen der Laufzeit abhängig vom Inhalt der Strings, möglicherweise auch in AU3.
Fazit:
- Die case-sensitive Suche ist in beiden Sprachen recht gut gelöst. Der Faktor von 4 - 4,5 zu Ungunsten von AU3 zeigt aber, dass noch etwas machbar wäre.
- Die case-insensitive Suche scheint in AU3 im Vergleich mit AHK und auch im Vergleich mit case-insensitive-locale bemerkenswert schlecht umgesetzt zu sein.
- Die case-insensitive-locale Suche ist (zumindest für diesen speziellen Fall) in AU3 im Vergleich zu AHK recht gut gelöst. Der Nachteil schmilzt auf einen Faktor von ~ 2.
Der These, dass AHK schneller sein muss, weil AU3 den umfangreicheren Interpreter hat, kann ich mich nicht anschließen. Die klare Sprachstruktur von AU3 sollte eher ein Vorteil sein. Ich bin allerdings auch der Meinung, dass die Entwickler auf Performancemängel angesprochen werden sollten, fühle mich aber nicht berufen, das selbst zu tun.