 |
AutoHotkey Community Wir helfen uns gegenseitig aus der Patsche
|
| Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
| Autor |
Nachricht |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Fr Feb 29, 2008 1:21 am Titel: |
|
|
Selbst wenn Chuckys code so funktionieren würde (sorry!) dann ist er noch doppelt so langsam wie der bisherige Testsieger von DerRaphael (und meine kleine Abwandlung).
Meine erste Version im englischen Forum (noch etwas konfus) ist allerdings um den Faktor 20 langsamer.
Ich hab hiernoch eine Abwandlung nach Laszlo, nur mit StringReplace.
Aber auch ein erstes und letztes Leerzeichen um den String wird entfernt:
| Code: |
delsp1(s){
StringReplace, s, s,%a_tab%,%a_space%, All
StringReplace s, s, %A_Space% %A_Space%, %A_Space%, All ; 21
StringReplace s, s, %A_Space% %A_Space%, %A_Space%, All ; 6
StringReplace s, s, %A_Space% %A_Space%, %A_Space%, All ; 3 -> 1
StringReplace s, s, %A_Space%%A_Space%, %A_Space%, All ; 2 -> 1
StringReplace s, s, %A_Space%%A_Space%, %A_Space%, All ; 2 -> 1
s =%a_space%%a_space%%s%%a_space%%a_space%
StringReplace, s, s,%a_space%%a_space%%a_space%,,all
StringReplace, s, s,%a_space%%a_space%,,all
return %s%
} |
Und der Klopper ist dennoch schneller als die Regular Expressions. |
|
| Nach oben |
|
 |
denick
Anmeldedatum: 15.09.2006 Beiträge: 1199 Wohnort: Berlin
|
Verfasst am: Sa März 01, 2008 12:50 pm Titel: |
|
|
Moin,
das hat mir natürlich keine Ruhe gelassen.
Nach vielem Probieren lassen sich für das gegebene Problem mehrere Feststellungen treffen:
1. Die relative Performance des RegEx sinkt mit der Komplexität des Suchausdrucks.
2. Die relative Performance des RegEx sinkt mit der Länge des Suchstrings.
3. Der RegEx wird relevant stärker vom umgebenden System beeinflusst (CPU, RAM, andere Tasks).
4. Der RegEx profitiert in diesem Fall durchaus von der Option S(tudy).
In diesem Beispiel werden ja nicht komplette Dateiinhalte sondern nur einfache Zeichenfolgen verarbeitet. Die Strings in DerRaphael's Benchmark sind deshalb meiner Meinung nach untypisch lang (obwohl die u.a. Funktion auch dort gewinnen kann). Nach Tests auf zwei recht unterschiedlichen Rechnern hat dann letztlich folgende Funktion gewonnen:
| Code: | DelSp2(Str)
{
StringReplace, Str, Str, %A_Tab%, %A_Space%, All
Str := RegExReplace(Str, "S)\s+", " ")
If (SubStr(Str, 1, 1) = " ")
Str := SubStr(Str, 2)
If (SubStr(Str, 0) = " ")
Str := SubStr(Str, 1, -1)
Return Str
} |
Ohne S(tudy) schlägt sie sich auch nicht wirklich schlecht, verliert aber mit wachsender Stringlänge.
Und hier ist der passende Benchmark (trau keinem, den Du nicht selbst geschrieben hast):
| Code: | #NoEnv
; SetBatchLines, -1
AutoTrim, Off
TxT := "`t`t The `t`t Quick Brown Fox Jumps`tBrown Over the"
. " Lazy Dog`t"
Str := Txt
Erg := ""
; freq enthält qpc einheiten per sek
DllCall("QueryPerformanceFrequency", "int64*", Frequenz)
MsgBox, 0
, Funktionsnachweis:
, % "DelSp1: >" . DelSp1(Str) . "<`n`nDelSp2: >" . DelSp2(Str) . "<"
SetFormat, Float, 0.8
OL := 20 ; Äußere Schleife
IL := 10 ; Innere Schleife
Loop, %OL%
{
VarSetCapacity(Out, StrLen(Str))
Critical
DllCall("QueryPerformanceCounter", "Int64*", Start)
Loop, %IL%
{
Out := DelSp1(Str)
}
DllCall("QueryPerformanceCounter", "Int64*", Ende)
Critical, Off
T1 := (Ende - Start) / Frequenz / IL
Critical
DllCall("QueryPerformanceCounter", "Int64*", Start)
Loop, %IL%
{
Out := DelSp2(Str)
}
DllCall("QueryPerformanceCounter", "Int64*", Ende)
Critical, Off
T2 := (Ende - Start) / Frequenz / IL
Erg .= StrLen(Str) . "|" . T1 . "|" . T2 . "`n"
Loop, %A_Index%
Str .= TxT
}
Gui, Margin, 0, 0
Gui, Font, s10, Tahoma
Gui, Add, Text, , Äußere Schleife`t: %OL%`nInnere Schleife`t: %IL%
Gui, Add, ListView
, Readonly NoSort NoSortHdr Grid R%OL% w400
, Stringlänge |Zeit DelSp1 | Zeit DelSp2 |DelSp2 `%
Loop, Parse, Erg, `n
{
If (A_Index > OL) {
Break
}
StringSplit, F, A_LoopField, |
LV_Add("", F1, F2, F3, Round(F3 * 100 // F2))
}
LV_ModifyCol(1, "Center")
Gui, Show, , Durchschnittszeiten
Return
GuiClose:
GuiEscape:
ExitApp
DelSp1(Str)
{
StringReplace, Str, Str, %A_Tab%, %A_Space%, All
Loop,
If (InStr(Str, " "))
StringReplace, Str, Str, %A_Space%%A_Space% ,%A_Space%, All
Else
Break
If (SubStr(Str, 1, 1) = " ")
Str := SubStr(Str, 2)
If (SubStr(Str, 0) = " ")
Str := SubStr(Str, 1, -1)
Return Str
}
DelSp2(Str)
{
StringReplace, Str, Str, %A_Tab%, %A_Space%, All
Str := RegExReplace(Str, "S)\s+", " ")
If (SubStr(Str, 1, 1) = " ")
Str := SubStr(Str, 2)
If (SubStr(Str, 0) = " ")
Str := SubStr(Str, 1, -1)
Return Str
} |
_________________ Hilfe zur Hilfe
(de)nick |
|
| Nach oben |
|
 |
DerRaphael
Anmeldedatum: 09.01.2008 Beiträge: 1037 Wohnort: Zuhause
|
Verfasst am: Sa März 01, 2008 2:24 pm Titel: |
|
|
chapeau, denick
 _________________
|
|
| Nach oben |
|
 |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Sa März 01, 2008 7:20 pm Titel: |
|
|
Ja komisch das man sich an so etwas festbeißen kann. Aber geht mir auch so.
Ich hab mal in deinem Benchmark den outerloop mit
Process, priority, ,Realtime laufen lassen. Die Ergebnisse scheinen dann reproduzierbarer zu sein.
 |
|
| Nach oben |
|
 |
Chucky
Anmeldedatum: 07.01.2006 Beiträge: 646 Wohnort: Powerland
|
Verfasst am: Sa März 01, 2008 10:37 pm Titel: |
|
|
Die Geschwindigkeit läßt sich noch ordentlich erhöhen, wenn man Maschinencode-Funktionen nutzt.
Diese Methode läuft natürlich außer Konkurrenz, denn sie benutzt nicht die üblichen AutoHotkey-Funktionen, wie DelSp1() und DelSp2().
Trotzdem ist sie erwähnenswert, da sie trotz ihrer hohen Geschwindigkeit komplett im Script integriert ist.
Grundlage ist die MCode-Funktion des genialen Laszlo aus dem amerikanischen Forum.
Man kann sich damit temporäre Maschinencode-Funktionen erzeugen, die naturgemäß wesentlich schneller sind als zu interpretierender Code.
Der Maschinencode läßt sich z. B. mit ziemlich jedem Compiler generieren, der DLLs erzeugen kann, z. B mit dem freien GNU-C-Compiler Dev-Cpp, den ich benutze.
Ich habe also eine DelSpaces-Funktion geschrieben (jetzt auch mit Tab->Space-Wandlung ) und in eine DLL kompiliert. Die DLL disassembliert man, um an den Hex-Code der Funktion zu kommen. Den Code aus dem Listing befreit man von Speicheradressen, Mnemonics, Spaces usw. und reduziert ihn damit auf den hexadezimalen Maschinencode.
MCode schreibt den Maschinencode, der in hexadezimaler Form im Script vorliegt, in eine Variable. AutoHotkey kann danach diese Variable als Funktion benutzen, indem es sie mit DllCall() aufruft. Die Funktion verhält sich dann genauso wie eine mit DllCall("LoadLibrary", ins RAM geladene Funktion einer DLL.
@denick
Schönes, übersichtliches Benchmark-Script, das werde ich wohl häufiger benutzen!
Eine Frage, wieso hast Du 'SetBatchLines, -1' auskommentiert? Und: 'Process, priority, , High' macht den Test noch unabhängiger von anderen Prozessen.
Ich habe es mal zur Gegenüberstellung um eine dritte DelSp-Funktion erweitert und das Geschwindigkeitsverhältnis zu DelSp2() in die letzte Spalte geschrieben.
Noch zwei Anmerkungen:
- Meine Funktion DelSp3() bearbeitet den ihr übergebenen String, daher muß er vor dem Funktionsaufruf an die Out-Variable übergeben werden statt nachher, damit die Originalvariable nicht zerstört wird. Deshalb der etwas seltsam anmutende Aufruf 'Out := Str, DllCall(&DelSp3, "str", Out)'. (Die Gründe dafür später, wenn's interessiert.)
- Der Test mißt die reine Arbeitszeit der Funktion, nicht aber die Zeit, die MCode braucht, um die Funktion zu erstellen. MCode braucht natürlich nur einmal vor dem ersten Aufruf gestartet zu werden; AutoHotkey braucht allein zum Starten ein Vielfaches dieser Zeit. Für den Fall, daß man es trotzdem nachmessen will, muß an den Stellen mit XXXXX ent- bzw. auskommentiert werden.
| Code: | #NoEnv
; Maschinencode aus einer DLL
DSCode =
(Join
5589E583EC0C8B45088945FC8B45088945F8C645F6018B45FC0FB6008845F7807DF700751B807DF600740D8B
45F83B450876058D45F8FF088B45F8C60000EB40807DF7207408807DF7097402EB17807DF60075258B45F8C6
00208D45F8FF00C645F601EB148B45F889C20FB645F788028D45F8FF00C645F6008D45FCFF00EB968B4508C9C3
)
; Erzeugung einer Funktion aus dem Maschinencode
MCode(DelSp3, DSCode) ; XXXXX auskommentieren für Benchmark incl. Funktionserzeugung
; Laszlos MCode
MCode(ByRef code, hex) { ; allocate memory and write Machine Code there
VarSetCapacity(code,StrLen(hex)//2)
Loop % StrLen(hex)//2
NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char")
}
; SetBatchLines, -1
AutoTrim, Off
TxT := "`t`t The `t`t Quick Brown Fox Jumps`tBrown Over the"
. " Lazy Dog`t"
Str := Txt
Erg := ""
; freq enthält qpc einheiten per sek
DllCall("QueryPerformanceFrequency", "int64*", Frequenz)
Out := Str, DllCall(&DelSp3, "str", Out)
MsgBox, 0
, Funktionsnachweis:
, % "DelSp1: >" . DelSp1(Str) . "<`n`nDelSp2: >" . DelSp2(Str) . "<`n`nDelSp3: >" . Out . "<"
SetFormat, Float, 0.8
OL := 20 ; Äußere Schleife
IL := 10 ; Innere Schleife
Loop, %OL%
{
VarSetCapacity(Out, StrLen(Str))
Critical
DllCall("QueryPerformanceCounter", "Int64*", Start)
Loop, %IL%
{
Out := DelSp1(Str)
}
DllCall("QueryPerformanceCounter", "Int64*", Ende)
Critical, Off
T1 := (Ende - Start) / Frequenz / IL
Critical
DllCall("QueryPerformanceCounter", "Int64*", Start)
Loop, %IL%
{
Out := DelSp2(Str)
}
DllCall("QueryPerformanceCounter", "Int64*", Ende)
Critical, Off
T2 := (Ende - Start) / Frequenz / IL
Critical
DllCall("QueryPerformanceCounter", "Int64*", Start)
Loop, %IL%
{
; XXXXX Die nächsten beiden Zeilen entkommentieren für Benchmark incl. Funktionserzeugung
; If Not DelSp3
; MCode(DelSp3, DSCode)
Out := Str, DllCall(&DelSp3, "str", Out)
}
DllCall("QueryPerformanceCounter", "Int64*", Ende)
Critical, Off
T3 := (Ende - Start) / Frequenz / IL
Erg .= StrLen(Str) . "|" . T1 . "|" . T2 . "|" . T3 . "`n"
Loop, %A_Index%
Str .= TxT
}
Gui, Margin, 0, 0
Gui, Font, s10, Tahoma
Gui, Add, Text, , Äußere Schleife`t: %OL%`nInnere Schleife`t: %IL%
Gui, Add, ListView
, Readonly NoSort NoSortHdr Grid R%OL% w500
, Stringlänge |Zeit DelSp1 | Zeit DelSp2 | Zeit DelSp3 |DelSp3/DelSp2 `%
Loop, Parse, Erg, `n
{
If (A_Index > OL) {
Break
}
StringSplit, F, A_LoopField, |
LV_Add("", F1, F2, F3, F4, Round(F4 * 100 // F3))
}
LV_ModifyCol(1, "Center")
Gui, Show, , Durchschnittszeiten
Return
GuiClose:
GuiEscape:
ExitApp
DelSp1(Str)
{
StringReplace, Str, Str, %A_Tab%, %A_Space%, All
Loop,
If (InStr(Str, " "))
StringReplace, Str, Str, %A_Space%%A_Space% ,%A_Space%, All
Else
Break
If (SubStr(Str, 1, 1) = " ")
Str := SubStr(Str, 2)
If (SubStr(Str, 0) = " ")
Str := SubStr(Str, 1, -1)
Return Str
}
DelSp2(Str)
{
StringReplace, Str, Str, %A_Tab%, %A_Space%, All
Str := RegExReplace(Str, "S)\s+", " ")
If (SubStr(Str, 1, 1) = " ")
Str := SubStr(Str, 2)
If (SubStr(Str, 0) = " ")
Str := SubStr(Str, 1, -1)
Return Str
} |
__________________________________________
Created with BBCodeWriter 7.0 - the one and only  |
|
| Nach oben |
|
 |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Sa März 01, 2008 11:41 pm Titel: |
|
|
In kürze will ich nur sagen :! Unglaublich !
Über den seltsamen Aufruf mußt Du uns (mich) tatsächlich später noch aufklären.
Zu ; SetBatchLines, -1 steht im Helpfile :
In v1.0.47+, turning on Critical also puts SetBatchLines -1 into effect for the current thread.
Danke für dieses "Ding"!
 |
|
| Nach oben |
|
 |
denick
Anmeldedatum: 15.09.2006 Beiträge: 1199 Wohnort: Berlin
|
Verfasst am: So März 02, 2008 8:33 am Titel: |
|
|
Moin Chucky,
"! Unglaublich !" halte ich mal für stark untertrieben.
| Zitat: | Der Maschinencode läßt sich z. B. mit ziemlich jedem Compiler generieren, der DLLs erzeugen kann, z. B mit dem freien GNU-C-Compiler Dev-Cpp, den ich benutze.
Ich habe also eine DelSpaces-Funktion geschrieben (jetzt auch mit Tab->Space-Wandlung Smile) und in eine DLL kompiliert. Die DLL disassembliert man, um an den Hex-Code der Funktion zu kommen. Den Code aus dem Listing befreit man von Speicheradressen, Mnemonics, Spaces usw. und reduziert ihn damit auf den hexadezimalen Maschinencode. | Wenn's weiter nichts ist?
| Zitat: | | Eine Frage, wieso hast Du 'SetBatchLines, -1' auskommentiert? Und: 'Process, priority, , High' macht den Test noch unabhängiger von anderen Prozessen. | Zu SetBatchLines hat ja haichen schon geantwortet. Process, Priority habe ich noch nie benutzt und deshalb auch nicht einmal daran gedacht. Ich werde es mir aber merken.
An die Künstler unter uns: Rollt die Transparente wieder ein!  _________________ Hilfe zur Hilfe
(de)nick |
|
| Nach oben |
|
 |
Milchmann
Anmeldedatum: 10.12.2007 Beiträge: 66
|
Verfasst am: Mo März 03, 2008 10:03 am Titel: |
|
|
Hallo, wie kann ich bei der genannte Version über
mehrere Zeilen die Leerzeichen und die TABs entfernen. Es geht um mehrere durch Enter getrennte Zeilen. Bei mir klappt es nicht mit der Variablen.
Danke Bert |
|
| Nach oben |
|
 |
BoBo¨ Gast
|
Verfasst am: Mo März 03, 2008 10:54 am Titel: |
|
|
Wie hälst du denn den Mehrzeiler vor? In einer Variablen (zB Clipboard)?
Oder Zeilen in einer Datei??
| Code: | Loop, Read, Mein.txt
DelSp(A_LoopReadLine)
.
.
. |
| Code: | Loop, Parse, ClipBoard, `n`r`
{
DelSp(A_LoopField)
ParsedContent .= A_LoopField "`n"
}
MsgBox % ParsedContent
.
. |
|
|
| Nach oben |
|
 |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Mo März 03, 2008 11:52 am Titel: |
|
|
Welche Version benutzt Du denn?
Die Binärcode Version entfernt bei mir keine Zeilentrenner.
DSCode = .... MCode(DelSp3, DSCode) müssen vor dem Aufruf von Out := Str, DllCall .. stehen.
Falls Du eine der Versionen mit RegexpReplace benutzt, mußt Du \s gegen \h
tauschen, damit \n oder \r nicht entfernt werden. |
|
| Nach oben |
|
 |
Milchmann
Anmeldedatum: 10.12.2007 Beiträge: 66
|
Verfasst am: Mo März 03, 2008 1:56 pm Titel: |
|
|
Hallo, ich möchte aus dem Clipboard mehrere Zeilen auslesen lassen und diese dann die Leerzeichen etc. entfernen. Da funktioniert aber dein Skript:
delspaces(str) nicht.
Wie muss das Skript richtig aufgebaut sein, damit es funktioniert?
PS: Wie kann ich einer Variablen einen mehrzeiligen Text zuweisen
Danke Bert
Wie muss ich |
|
| Nach oben |
|
 |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Mo März 03, 2008 2:47 pm Titel: |
|
|
| Dann zeig doch mal was Du hast. Falls es Teil eines größeren Skripts ist, am besten nur den Teil mit dem ClipBoard und dem DeleteSpaces. |
|
| Nach oben |
|
 |
haichen
Anmeldedatum: 10.06.2007 Beiträge: 92
|
Verfasst am: Mo März 03, 2008 3:36 pm Titel: |
|
|
Geht alles wie 'ne eins.
Hier nur zwei Beispiele:
| Code: | #d:: msgbox, % delspaces5(clipboard)
delspaces5(str){
str :=RegExReplace(Str,"\h{2,}", " ")
str :=RegExReplace(Str,"(^\h)|(\h$)","")
return %str%
}
|
| Code: | MCode(DelSp3)
#d:: msgbox, % delspaces8(clipboard)
delspaces8(str){
global DelSp3
Out := Str, DllCall(&DelSp3, "str", Out)
return out
}
MCode(ByRef code) { ; allocate memory and write Machine Code there
hex =
(Join
5589E583EC0C8B45088945FC8B45088945F8C645F6018B45FC0FB6008845F7807DF700751B807DF600740D8B
45F83B450876058D45F8FF088B45F8C60000EB40807DF7207408807DF7097402EB17807DF60075258B45F8C6
00208D45F8FF00C645F601EB148B45F889C20FB645F788028D45F8FF00C645F6008D45FCFF00EB968B4508C9C3
)
VarSetCapacity(code,StrLen(hex)//2)
Loop % StrLen(hex)//2
NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char")
}
|
|
|
| Nach oben |
|
 |
Gast
|
Verfasst am: Mo März 03, 2008 8:34 pm Titel: |
|
|
Hallo Haichen,
Du hast mit Deinen Funktionen recht,
diese laufen:
| Code: |
Loop, Parse, ClipBoard, `n`r`
{
DelSp(A_LoopField)
ParsedContent .= A_LoopField "`n"
}
MsgBox % ParsedContent |
Dort kommt call to non exist Funktion Wieso
Mein Clipboard enthält evtl. diese Daten:
[code]
NeuJahr=01.01.Jahr
Karfreitag=Ostersonntag-2
Ostermontag=Ostersonntag+1
ChristiHimmelfahrt=Ostersonntag+39
Pfingstsonntag=Ostersonntag+49
Pfingstmontag=Ostersonntag+50
TagDerArbeit=01.05.Jahr
TagDerDeutschenEinheit=03.10.Jahr
Reformationstag=31.10.Jahr
Buß- und Bettag
HeiligAbend=24.12.Jahr
ErsterWeihnachtstag=25.12.Jahr
ZweiterWeihnachtstag=26.12.Jahr
Silvester=31.12.Jahr
)
;Rosenmontag=Ostersonntag-48
;Faschingsdienstag=Ostersonntag-47
;Aschermittwoch=Ostersonntag-46
;Gründonnerstag=Ostersonntag-3
; HlDreiKönige=06.01.Jahr
; FrankfurterWäldchestag=Ostersonntag+51
; Karsamstag=Ostersonntag-1
; Fronleichnam=Ostersonntag+60
; AugsburgerFriedensfest=08.08.Jahr
; MariäHimmerfahrt=15.08.Jahr
; Alleheiligen=01.11.Jahr
; MariäEmpfängnis=08.12.Jahr [/code
Danke Bert] |
|
| Nach oben |
|
 |
BoBo¨ Gast
|
Verfasst am: Mo März 03, 2008 8:46 pm Titel: |
|
|
| Zitat: | | Dort kommt call to non exist Funktion Wieso | Weil da wo die drei Punkte angeführt sind, die Funktion stehen sollte. |
|
| Nach oben |
|
 |
|
|
Du kannst Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum antworten.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|