Entwickler-Ecke
Algorithmen, Optimierung und Assembler - Wieso schneller trotz weniger?
walter_b - Fr 18.07.08 18:30
Titel: Wieso schneller trotz weniger?
Hallo zusammen
Delphi verblüfft mich echt immer wieder aufs Neue. Und diesmal kann ich mir es echt sowas von nicht erklären, vielleicht wisst ihr ja weiter. Mich würde bloss interessieren, wieso die eine Variante schneller ist als die andere.
Diese Variante dauert ebenfalls bei 1 Mia Durchgängen 3600ms. Der Vergleich: Len wird oben als Variable gelöscht, Len:=0 wird rausgestrichen und Len:=max(Y,Len) wird entfernt. Der Rest ist total identisch. Len wird im ganzen Programm nirgends sonst verwendet, ist noch ein Überbleibsel eines vorher durchgeführten Versuches.
Kann jemand von euch mir das erklären? Steht Delphi drauf, mehr zu tun als notwendig? :P
Hidden - Fr 18.07.08 20:37
Hi,
imho ehrer Algorithmen und Optimierung, oder?
hast du schonmal die Optimierung ausgeschaltet?({$O-} {$O+})
mfG,
walter_b - Fr 18.07.08 21:09
Hidden hat folgendes geschrieben: |
Hi,
imho ehrer Algorithmen und Optimierung, oder?
hast du schonmal die Optimierung ausgeschaltet?({$O-} {$O+})
mfG, |
Sorry? Ich kann dir nicht folgen. Zuerst einmal: Was hatte das kleingedruckte zu bedeuten? Und welche Optimierung ausschalten? Und was ist {$0-} {$0+}? :oops: :?!?:
Timosch - Fr 18.07.08 21:42
Das sind Compilerdirektiven, mit denen du die Optimierung ausschalten kannst.
walter_b - Fr 18.07.08 21:47
Und wo soll ich die hinsetzen? Und beide oder nur einen?
Nils:D - Fr 18.07.08 21:55
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| {$0-} procedure blubb; begin ... end; {$0+} |
Du musst die Optimierung vor der gewünschten Prozedur deaktivieren und hinterher wieder aktivieren. Compiler-Optimierungen sorgen für ein schnelleres und kleineres Programm, können daher den einen Algorithmus verschnellern, den anderen nicht, da ein gewisser Fall dort nicht gegeben sein könnte.
alzaimar - Fr 18.07.08 21:58
Das Phänomen ist doch, das der Code schneller wird, wenn man etwas weg nimmt. Was wird denn da -bitte schön- optimiert? ich tippe eher auf einen kleinen Fehler (oder eine Eigenheit) beim Übersetzen in Maschinencode. Es kann sein, das die Heuristik, die die Variablen auf Register verteilt, bei der Variante 1 einfach besser funktioniert, als bei #2.
walter_b - Fr 18.07.08 22:11
Nils:D hat folgendes geschrieben: |
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| {$0-} procedure blubb; begin ... end; {$0+} |
Du musst die Optimierung vor der gewünschten Prozedur deaktivieren und hinterher wieder aktivieren. Compiler-Optimierungen sorgen für ein schnelleres und kleineres Programm, können daher den einen Algorithmus verschnellern, den anderen nicht, da ein gewisser Fall dort nicht gegeben sein könnte. |
Invalid Compiler directive: '$0'
Naja, ist ja eigentlich auch egal. Ich lasse es einfach drin, da es so um das doppelte schneller ist. Warum sich mit weniger zufrieden geben? ;) (Auch wenn jeder, der den Code anschaut, denken wird, dass ich spinne :D)
Nils:D - Fr 18.07.08 22:19
Hm, dürfte eigentlich funktionieren. Aber für die Zukunft noch eine Empfehlung: Schreibe ein Kommentar hin, warum du es so gemacht hast und dass diese Stelle eventuell bei einem anderen Compiler nicht schneller oder sogar leicht langsamer sein dürfte. Es kann nämlich gut sein, dass ein neuerer/älterer Compiler das anders kompiliert.
Guenther A. - So 20.07.08 00:25
Titel: Ist max eine externe Function?
Ist die max-Function eine exteren Function, das würde einiges erklären. Jedenfalls kenn mein Delphi-7 die Funktion nicht. Die Befehle in der Schleife sind sehr schnell, da hätte der Aufruf von max schon mit den erforderlichen Stack-Operationen einen erhebliche Anteil.
Tipp: Sie Die einmal das CPU-Fenster für den Code an (Breakpoint setzen). Auc wenn Du denn Assembler-Code nicht genau verstehst, so gibt es doch eine Eindruck, was passiert. F7 funktioniert auch da.
Gruß Guenther
Martok - So 20.07.08 00:49
walter_b hat folgendes geschrieben: |
[Invalid Compiler directive: '$0' |
Nicht Null, O wie Optimization.
Guenther A. hat folgendes geschrieben: |
Jedenfalls kenn mein Delphi-7 die Funktion nicht. |
delfiphan - So 20.07.08 10:04
Hast du die zwei Prozeduren auch wirklich unter exakt identischen Voraussetzungen laufen gelassen?
walter_b - So 20.07.08 12:56
delfiphan hat folgendes geschrieben: |
Hast du die zwei Prozeduren auch wirklich unter exakt identischen Voraussetzungen laufen gelassen? |
Ja, habe ich!
Und da es gleich zum Thema passt, langsam habe ich echt das Gefühl, dass Delphi masochistisch veranlagt ist. Schaut euch mal dies an:
Variabeln:
Delphi-Quelltext
1: 2: 3: 4: 5:
| var X, Y: Integer; Wort: String; fChar: PChar; binlm, bigpass, endhash: string; |
Schnellere Variante (136 Sekunden bei 1 Mia Durchgängen):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); alpha:=HexStrToString(alpha); beta:=DecodeText('KGS!@#$%',ende); beta:=HexStrToString(beta); binlm:=alpha+beta; endhash:=StringToHexStr(binlm); |
Langsamere Variante (141 Sekunden bei 1 Mia Durchgängen):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); beta:=DecodeText('KGS!@#$%',ende); binlm:=alpha+beta; |
Unterschied: Bei der oberen Variante wird sinnloserweise zuerst die Hex-Darstellung zu einem String umgewandelt, und nachher wieder zurück. Das Ergebnis ist bei beiden total identisch.
Hier noch die Umwandlungsfunktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| function HexStrToString(const value:string):string; begin SetLength(Result, Length(value) div 2); if Length(value) > 0 then HexToBin(PChar(value), PChar(Result), Length(value)); end;
function StringToHexStr(const value:string):string; begin SetLength(Result, Length(value)*2); if Length(value) > 0 then BinToHex(PChar(value), PChar(Result), Length(value)); end; |
Hat jemand für das eine Erklärung? Hier kann es ja nicht mehr wirklich die Begründung sein, dass die zusätzliche Variabel alles vielleicht ein bisschen schneller macht. Bin mal gespannt auf eure Vermutungen/Erklärungen.
Edit: anf, ende, alpha, beta, endhash binlm wurde alles initialisiert und mit der richtigen Länge definiert.
Delete - So 20.07.08 13:05
du weisst ja auch, dass die beiden Varianten was ganz anderes machen :roll:
walter_b - So 20.07.08 13:13
Grenzgaenger hat folgendes geschrieben: |
du weisst ja auch, dass die beiden Varianten was ganz anderes machen :roll: |
Ehrlichgesagt: Nein, das weiss ich nicht. Zuerst wird ja der Hash erstellt, welcher dann in zwei Teilen vorhanden ist, zwar in anf und in ende. Danach wird bei der schnelleren Variante das ganze in zwei Strings umgeformt (also aus dem Hexidezimalsystem), zusammengesetzt und wieder ins Hexidezimalsystem zurückverwandelt. Bei der langsameren Variante werden die beiden Teile zusammengesetzt und fertig. Wieso ist es schneller, wenn ich zuerst sinnlos umwandle und wieder zurückwandle? Das Ergebnis ist nämlich total identisch.
Hidden - So 20.07.08 13:47
Hi,
Ich hab das ganze mal gehighlighted:
Schnellere Variante (136 Sekunden bei 1 Mia Durchgängen):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| bigpass := Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0 +#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf := ParityShift(Copy(Bigpass, 0, 7)); ende := Copy(Bigpass, 8, 7); ende := ParityShift(ende); alpha := DecodeText('KGS!@#$%', anf); alpha := HexStrToString(alpha); beta := DecodeText('KGS!@#$%', ende); beta := HexStrToString(beta); binlm := alpha + beta; endhash := StringToHexStr(binlm); |
Langsamere Variante (141 Sekunden bei 1 Mia Durchgängen):
1: 2: 3: 4: 5: 6: 7: 8:
| bigpass := Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0 +#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf := ParityShift(Copy(Bigpass, 0, 7)); ende := Copy(Bigpass, 8, 7); ende := ParityShift(ende); alpha := DecodeText('KGS!@#$%', anf); beta := DecodeText('KGS!@#$%', ende); binlm := alpha + beta; |
Ist die Deklaration von alpha/ß verschieden? imho hängst du einmal einen String an einen anderen an und einmal addierst du zwei Zahlenwerte..
Sollte es sich beide male um Strings handeln, ist die HexString-Darstellung wahrscheinlich einfach kürzer und deshalb dauert das anhängen weniger lang.
Hab jetzt aber auch gerade nicht die Zeit, mich tiefer einzuarbeiten. Schätze mal, dass ich mich oben verguckt habe mit einmal Strings aneinanderhängen und einmal zwei Zahlen addieren.
E: DIe Deklaration von a/ß fehlt ja auch :roll:
E2: Mit HexToBin fällt wohl die Kürzerer-String-Theorie weg^^
mfG,
walter_b - So 20.07.08 13:51
Nein, sind beides Mal zwei Strings. Sonst würde es ja auch nicht das selbe Ergebnis geben, wenn zwei Zahlenwerte zusammengerechnet werden würden.
Also sollte ich ab jetzt jedes mal, wenn ich zwei Strings zusammenfügen will, die zuerst umwandeln und dann wieder zurück? :D
Hidden - So 20.07.08 13:55
Hi,
Imho liegt es an deinen #0.. kA, was damit genau passiert. hab gerade kein Delphi da.. Könntest du mal das Ergebnis der HexStringToString ausgeben lassen?
mfG,
Delete - So 20.07.08 14:10
wo kommt denn hier das selbe raus?
V1: binlm := HexStrToString(DecodeText('KGS!@#$%', anf)) + HexStrToString(DecodeText('KGS!@#$%', ende));
V2: binlm := DecodeText('KGS!@#$%', anf) + DecodeText('KGS!@#$%', ende);
denke doch, dass das was anderes ist, oder? :roll:
walter_b - So 20.07.08 14:10
Nach dem DecodeText ist anf '695109ab020e401c' und ende 'aad3b435b51404ee'. Nach dem HexStrtoString sind beide Werte ziemlich hässlich, sehr unschöne Sonerzeichen. Nach dem alpha+beta sind die beiden "hässlichen" Strings einfach aneinander gehängt, und nach StringtoHexStr ist der Endstring '695109ab020e401caad3b435b51404ee'. Also einfach anf und ende aneinander gehängt.
walter_b - So 20.07.08 14:15
Grenzgaenger hat folgendes geschrieben: |
wo kommt denn hier das selbe raus?
V1: binlm := HexStrToString(DecodeText('KGS!@#$%', anf)) + HexStrToString(DecodeText('KGS!@#$%', ende));
V2: binlm := DecodeText('KGS!@#$%', anf) + DecodeText('KGS!@#$%', ende);
denke doch, dass das was anderes ist, oder? :roll: |
Nein, ist das selbe, da du bei Variante 1 noch etwas vergessen hast.
V1:
1: 2:
| binlm := HexStrToString(DecodeText('KGS!@#$%', anf)) + HexStrToString(DecodeText('KGS!@#$%', ende)); endhash:=StringToHexStr(binlm); |
Und genau der highlightete Teil Erstellt wieder den ursprünglichen String. Schau mein Posting vorher an mit den Textausgaben.
Delete - So 20.07.08 14:18
walter_b hat folgendes geschrieben: |
Nein, ist das selbe, da du bei Variante 1 noch etwas vergessen hast.
V1: Delphi-Quelltext 1: 2:
| binlm := HexStrToString(DecodeText('KGS!@#$%', anf)) + HexStrToString(DecodeText('KGS!@#$%', ende)); endhash:=StringToHexStr(binlm); | Und genau der highlightete Teil Erstellt wieder den ursprünglichen String. Schau mein Posting vorher an mit den Textausgaben. |
nein habe ich nicht. dieser teil fehlt bei deiner 2. variante.
walter_b - So 20.07.08 14:23
Grenzgaenger hat folgendes geschrieben: |
nein habe ich nicht. dieser teil fehlt bei deiner 2. variante. |
Schnellere Variante (136 Sekunden bei 1 Mia Durchgängen):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); beta:=DecodeText('KGS!@#$%',ende); alpha:=HexStrToString(alpha); beta:=HexStrToString(beta); binlm:=alpha+beta; endhash:=StringToHexStr(binlm); |
Langsamere Variante (141 Sekunden bei 1 Mia Durchgängen):
1: 2: 3: 4: 5: 6: 7:
| bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); beta:=DecodeText('KGS!@#$%',ende); binlm:=alpha+beta; |
Ich bin nicht sicher, aber ich glaube, wir sprechen aneinander vorbei. Ich sage, dass endhash von Variante 1 das selbe ist, wie binlm von Variante 2. Bei der ersten Variante wird zuerst umgewandelt und wieder zurückgewandelt (siehe Highlight), was jedoch nichts verändert, wie die Textausgabe bewiesen hat. In der zweiten Variante wird nichts Umgewandelt und folglich auch nicht zurückgewandelt.
Oder was genau meinst du?
Delete - So 20.07.08 14:25
und du meinst die variable: endhash ist die selbe wie die hier: binlm wenn es um die weiterverarbeitung geht?
:roll:
walter_b - So 20.07.08 14:28
Genau. Und die Textausgabe hat das auch gezeigt!? :?
Zitat: |
Nach dem DecodeText ist anf '695109ab020e401c' und ende 'aad3b435b51404ee'. Nach dem HexStrtoString sind beide Werte ziemlich hässlich, sehr unschöne Sonerzeichen. Nach dem alpha+beta sind die beiden "hässlichen" Strings einfach aneinander gehängt, und nach StringtoHexStr ist der Endstring '695109ab020e401caad3b435b51404ee'. Also einfach anf und ende aneinander gehängt. |
Ohne das HexStrToString und StringToHexStr wird aus '695109ab020e401c' und 'aad3b435b51404ee' ebenfalls '695109ab020e401caad3b435b51404ee'.
Anderer Meinung?
delfiphan - So 20.07.08 16:06
Identische Bedingung heisst auch, dass du nicht zuerst Funktion 1 und danach sofort Funktion 2 testest, weil die Reihenfolge durchaus einen Unterschied machen kann. Weiss nicht, in wiefern da der Cache und Branch Prediction eine Rolle spielen. Jedenfalls vertraue ich nur Vergleiche unter identischen Bedingungen. Kannst du mal das komplette kompilierbare Projekt posten.
Bei deinem ersten Beispiel ist bei mir der zweite Code ein bisschen schneller als der erste. Eigentlich so wie's zu erwarten wäre.
AxxiD - So 20.07.08 16:28
ich würd ja sagen dass dein pc einfach besser mit strings arbeiten kann also mit HexaStrings!
und genau das macht dann den unterschied
aber sei doch froh dass du dein programm hast optimieren können! ;)
MfG
alzaimar - So 20.07.08 16:35
AxxiD hat folgendes geschrieben: |
ich würd ja sagen dass dein pc einfach besser mit strings arbeiten kann also mit HexaStrings! |
:autsch: :autsch: Meine Vermutung ist eher, das die amerikanische CPU mit Umlauten nicht umgehen kann (Umlaute gibts im Englischen ja nicht).
Erinnert mich an einen Artikel in der Computer-Bild:
Frage: "Ich habe mir einen US-Film im AVI-Format downgeloadet, kann ihn mir aber nicht anschauen, ich höre nur den Ton. Wieso?"
Antwort Computer BILD:
"Ganz einfach, das amerikanische Video-Format NTSC ist inkompatibel zu unserem PAL-System. Besorgen Sie sich einen Software-converter, dann können Sie sich den Film anschauen"
:rofl:
Delete - So 20.07.08 16:47
alzaimar hat folgendes geschrieben: |
AxxiD hat folgendes geschrieben: | ich würd ja sagen dass dein pc einfach besser mit strings arbeiten kann also mit HexaStrings! |
:autsch: :autsch: Meine Vermutung ist eher, das die amerikanische CPU mit Umlauten nicht umgehen kann (Umlaute gibts im Englischen ja nicht). |
Sicher? Ich frag mich, wie er überhaupt das selbe rausbekommen kann, wenn die Variablen doch anders belegt werden. So, hier etwas zu sagen, ist nichts anderes als Kristallkugel zu lesen und
walter_b hat ja nicht wirklich vor, hier erhellend beizutragen. Immer nur verstümmelte Codefragmente, ich krieg die Krise :evil:
walter_b - So 20.07.08 18:10
Jungs, Jungs, nur keine Panik. :D Ich war gerade nicht im Forum.
Zuerst einmal an alzaimar: Darüber weiss ich bescheid, deshalb habe ich Umlaute gezielt ausgegrenzt! Hier spielen definitiv keine Umlaute mit.
@delfiphan: Hm... dann habe ich wohl ein getunetes Delphi mit einem Spezialcheat. ;) Aber so wies aussieht, liegts dann wohl an meinem Delphi.
@Grenzgaenger: Tut mir leid, wenn ich dich nerve, aber ich dachte immer, dass man hier im Forum lieber den Code sieht, als ständig das Projekt anhängt. Und wegen dem Code: Ich muss schnell bei meinen 27 Versionen des Programmes auf dem PC suchen, welche die richtige ist. Danach werde ich zwei Projekte durch editieren diesem Posting anhängen: Dasjenige mit dem max() und die zweite mit dem umwandeln. Bis gleich ;)
Edit: Hier hab ihr. Um zu vergleichen, müsst ihr jeweils die Auskommentierten Zeilen wieder aktivieren.
delfiphan - So 20.07.08 19:20
So wie's aussieht verwendet der Compiler für die Variablen andere Register bei den Fällen mit/ohne max(). So ist die Zählvariable X der Hauptschleife beim Fall von max() eax, und beim Fall ohne max() edi. Das könnte die Geschwindigkeitsdifferenz ausmachen.
Delete - So 20.07.08 19:54
Also, wie zu erwarten ist die erste procedure, im gegensatz zu deinen angaben, durchweg schneller.
ausserdem, es bleibt bei, dass die routinen etwas anderes machen. hier noch mal, deine beiden routinen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84:
| procedure Gen2(); var X, Y: Integer; Wort: String; fChar: PChar; binlm, bigpass: string; begin SetLength(Wort, 1); fChar := @Wort[1]; fChar^ := Chr(Ord('A') - 33);
for X := 0 to combs do begin Y := 1; inc(fChar^);
while Wort[Y] > 'z' do begin inc(Y);
if Y > Length(Wort) then begin SetLength(Wort, Y); Wort[Y] := '!'; end else inc(Wort[Y]);
Wort[Y - 1] := '!'; end; bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); beta:=DecodeText('KGS!@#$%',ende); binlm:=alpha+beta; end; end; procedure Gen3(); var X, Y: Integer; Wort: String; fChar: PChar; binlm, bigpass: string; begin SetLength(Wort, 1); fChar := @Wort[1]; fChar^ := Chr(Ord('A') - 33);
for X := 0 to combs do begin Y := 1; inc(fChar^);
while Wort[Y] > 'z' do begin inc(Y);
if Y > Length(Wort) then begin SetLength(Wort, Y); Wort[Y] := '!'; end else inc(Wort[Y]);
Wort[Y - 1] := '!'; end; bigpass:=Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0,0,14); anf:=parityshift(Copy(bigpass,0,7)); ende:=Copy(bigpass,8,7); ende:=parityshift2(ende); alpha:=DecodeText('KGS!@#$%',anf); alpha:=HexStrToString(alpha); beta:=DecodeText('KGS!@#$%',ende); beta:=HexStrToString(beta); binlm:=alpha+beta; endhash:=StringToHexStr(binlm); end; end; |
walter_b - So 20.07.08 20:37
Meine Angaben stimmen, wenn ich beide Prozeduren ausführe und beide Zeiten ausgeben lasse, dann ist bei mir diejenige mit dem umwandeln schneller. Wenn es bei dir anders ist, dann freue ich mich zumindest darüber, dass es bei mir dadurch schneller wird.
Und ich weiss nicht recht, wie ich deine Performance deuten soll, bzw. was bei den beiden Varianten anders laufen soll.
Delete - So 20.07.08 20:45
Hier hast du Recht, hier ist die Variante mit
Len schneller. Weshalb? - da solltest dir den generierten Assembler Code ansehen.
---
Moderiert von
Narses: Beiträge zusammengefasst---
walter_b hat folgendes geschrieben: |
Und ich weiss nicht recht, wie ich deine Performance deuten soll, bzw. was bei den beiden Varianten anders laufen soll. |
deine GEN routine ist die erste schneller, welche weniger macht. Wo die performance haengen bleibt, ist der Aufruf deiner (Sub-) Routinen, das bekommt die zweite Funktion besser hin. Hier solltest Dir das auch am bessten in Assembler ansehen und die Aufrufe manuell optimieren.
<HTH> GG
PS: hab noch mal die Zeitaufteilung nach Routinen für deine GEN Funktionen angehängt.
PPS: wenn du wirklich etwas für deine performance machen willst, solltest mal diese routine optimieren.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function DecodeText(Inputtext, Key: String): String; begin with TCipher_1DES.Create do try Mode := cmECBx; Init(Key); Result := EncodeBinary(Inputtext, TFormat_HEXL); finally Free; end; end; |
die frisst dir fast die ganze zeit auf
walter_b - So 20.07.08 22:17
Vielen Dank. Ich weiss, dass diese Funktion ein Problemfall ist, bin auch schon dabei, sie zu optimieren, auch wenn sie in der DEC steckt ;)
Aber das Problem ist, dass ich gerne 4 Mio in einer Sekunde erreichen würde. Was meinst du? Ist wohl bei weitem nicht erreichtbar, oder?
---
Moderiert von
Narses: Beiträge zusammengefasst---
Wie hast du diese Performances erstellt? Habe jetzt mal ein bisschen rumoptimiert, würde das gerne selbst erstellen können. :)
Lossy eX - Di 22.07.08 10:32
walter_b: Auch wenn das keine direkte Antwort auf deine Frage ist. Aber du solltest dich nicht zu sehr auf irgendwelche Messergebnisse hängen. Als kleines Beispiel. Ich hatte vor ein paar Tagen einen Code geschrieben der 6 MB Speicher X mal kopiert hat. Ich hatte die Anwendung 10 mal ausführ und hatte 10 unterschiedliche Messergebnisse bekommen. Die Zeiten lagen alle im Bereich zwischen 18 und 24 Sekunden. Was eine erhebliche Schwankung ist. Obwohl ich sonst nichts am Rechner gemacht hatte. Je nachdem ob Windows oder einer der anderen Prozesse gerade etwas machen wollte oder nicht. Deswegen bitte die Messergebniss nicht überbewerten. Und mindestens 5-10 mal testen und selbst dann kann man nur über den Daumen schätzen.
Was ich zum Beispiel lassen würde wären solche Konstelationen.
Delphi-Quelltext
1:
| Copy(AnsiUpperCase(Wort)+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0+#0, 0, 14); |
Um das mal zu verdeutlichen WAS ich damit meine habe ich einen Teil der CPU Ansicht als Anhang an das Post angefügt. Diese kleine Zeile Code ist ein
regelrechter Brocken und es mal deutlich zu sagen. Denn du hast eine Umwandlung eines Textes (Wort) in Großbuchstaben, 15 Strings die aneinander kopiert werden sollen und dann ein Abschneiden der überflüssigen Zeichen. Alles gepaart mit mehreren temporären Strings, die alloziiert und wieder freigegeben werden müssen.
Das ist jetzt nicht böse gemeint. Aber du solltest nicht den einfachsten Weg gehen sondern versuchen zu verstehen was genau da passiert. Um solche Stellen auf Anhieb zu sehen muss man so etwas aber schon häufiger mal gemacht haben. Das CPU Fenster ist genial für solche Fälle. Denn dieses Fenster zeigt den maschinen Code einzelner Anweisungen an. Ein bisschen wissen über Assambler ist dann leider von nöten. Aber nicht viel. Nur so sieht man relativ schnell was da eigentlich passiert.
Aber folgende Verbesserungen würde ich vorschlagen.
- Schau dir mal die Methoden an. Copy, AnsiUpperCase. Alle haben einen String als Paramater und einen als Rückgabewert. Rückgabewert bedeutet, dass ein neuer String erzeugt werden muss. Wenn du den String aber nicht mehr brauchst, dann kannst du für AnsiUpperCase zum Beispiel selber etwas schreiben mit einem Var Parameter. Dann änderst du die Zeichen innerhalb des einen Textes. Deine eigene AnsiUpperCase Methode sollte mit einem Mapping array arbeiten und einfach nur die Zeichen des Textes durch die innerhalb des Array ersetzen. Das Array enthält alle Zeichencodes unverändert aber die von Kleinbuchstaben wurden zu großbuchstaben.
- Dann würde ich Stringadditionen vermeiden wo es geht. Genau so auch Copy Aufrufe. Du könntest ParityShift doch so erweitern, dass du den originalen Text und die Positionsangaben übergibst. Parityshift würde sich dann nur noch auf diese Zeichen beziehen.
- Das Beschränken des Textes auf 14 Buchstaben kannst du auch über SetLength lösen. Dann muss der Text nur ein einziges mal angepasst werden. Und wenn du dir vorher merkst wie lang es war kannst du den Rest auch mit #0 auffüllen. Das Sinnvollste wäre aber vermutlich sogar noch, wenn der Text von Hause aus gleich 14 Zeichen lang wäre und du die Zeichen nicht zu Wort addieren würdest sondern nur an die Stelle 1, 2 oder 3 schreibst. Das fällt diese ganze Arbeit von Hause aus weg.
Die beste Optimierung ist, Arbeit die nicht gebraucht wird auch gar nicht erst zu machen. Denn das spart in jedem Fall die meiste Zeit.
Wenn du es wirklich wirklich schnell hinbekommen willst, dann solltest du evtl auch mit dem Gedanken spielen die Delphi Strings sein zu lassen. Durch die automatische Referenzzählung von Delphi holst du dir jede Menge versteckten Aufwand mit in dein Programm. Und es kann schnell mal passieren, dass dort Dinge gemacht werden die man nicht erahnt. Wärend, wenn du selber alles handhaben musst, dann bleibt dir nichts anderes übrig als genau zu wissen was passiert. Wird dadurch aber auch umständlicher und schwerer. Und ist natürlich kein Garant dafür, dass es schneller werden muss. Wenn man dabei unglückliche Sachen macht, dann kann es auch langsamer werden.
PS: Du solltest evtl. deine Methode HexStrToString überdenken! Denn diese kann über ihren Speicherbereich hinwegschreiben. Du nimmst für die Länge des Rückgabewertes die Länge des Parameters DIV 2. Beispiel. Du übergibst einen Hexwert mit einer Ungeraden länge. Zu Beispiel $FF FF FF. Dann ist die Länge 3. 3 DIV 2 ist 1. Eigentlich 1,5 aber das Komma 5 fällt weg. $FF FF FF ist aber Binär folgendes 1111 1111 1111. Entsprechend werden 4 Bits des 2ten Wertes mit gesetzt. Das liegt also außerhalb deines eigentlichen Strings.
walter_b - Di 22.07.08 13:04
Vielen Dank für deine Ausführlichen Erklärungen. Dass diese Textstelle nicht optimal ist, war mir von Anfang an klar. Ich hatte 6 verschiedene Versionen, wie diese Aufgabe angegangen wird und dies ist eine davon (und auch nicht die beste ;)). Da bei diesem Falle das Mysterium mit dem HexToString aufgetaucht ist, habe ich den Code einfach mal so gepostet. Aber den habe ich mittlerweile auch schon optimiert. Im Moment sieht die Stelle wie folgt aus:
Delphi-Quelltext
1: 2:
| for I:=Length(Wort) to 14 do Wort:=Wort+#0; |
Und bis und mit dieser Funktion erreicht der Programmteil 4 Millionen Durchgänge in 16 Millisekunden. Da wären wir gleich beim nächsten Punkt, nähmlich den Zeitangaben. Ich mache immer mindestens 3 mal den gleichen Durchgang, und bei mir waren bisher noch nie grössere Abweichungen als 50ms festzustellen. Aus diesem Grund schaue ich die Messwerte, welche ich erhalte, doch als einigermassen repräsentativ an. Mit Dem Bild, wie du es angefügt hast, kann ich irgendwie nicht besonders viel anfangen. Ich sehe zwar, dass er den einen Schritt 14 mal macht, jedoch wäre für mich noch interessant zu sehen, wie lange er auch dafür benötigt. Aus diesem Grund interessiert es mich, wie Grenzgaenger seine Performance erstellen konnte. Bin wohl zu noobig für deine Version. ;)
Und keine Angst, das mit dem HexToString war nicht wirklich meine Absicht, dies so im Code zu lassen. ^^
Lossy eX - Di 22.07.08 13:28
CPU-Ansicht: Na ja. Man muss dafür ein bisschen Assembler können. Das braucht man zu Zeiten der Hochsprachen normal aber nur für wirklich spezielle Sachen. Aber nur eines dazu. Jedes mal wenn dort steht "CALL Blahhh", dann wird eine Methode aufgerufen. Das ist beim Verändern von Strings eigentlich jedes mal der Fall. Diese LStr* Methoden sind zwar schnell aber wenn man die einige Mio mal aufruft, dann kostet das Aufrufen der Methoden alleine auch schon seine Zeit.
Das zweite Dokument sieht so aus als ob das aus
aqTime [
http://www.automatedqa.com/products/aqtime/index.asp] stammt. Das ist ein Profiler. Der misst wie lange einzelne Methoden aufgerufen wurden. Auf der Seite gibt es auch eine 30 Tage Testversion. Für Privat aber kaum vernünftig erschwinglich. Aber für Firmen sicher jeden Euro Wert.
Delete - Di 22.07.08 21:14
yep, Lossy hat recht, es ist AQTime. Ein normaler profiler. Denke aber, dass es der falsche ansatz ist, welchen du verfolgst. bevor man sich mit der performance befasst, sollte man mal sehen, dass das programm überhaupt läuft... anschliessend, dass es vernünftig strukturiert und fehlerfrei ist... erst dann, schaut man, wie man es etwas schneller machen könnt... das heisst, man schaut sich einfach mal an, was man gemacht hat und wie man es eleganter machen könnte... in einem zweiten schritt schaut man, dass man dass man groebere schnitzer ausbügelt.. z.b. man überlegt sich, ob die datenstrukturen die man verwendet auch optimal zum algo passen und optimiert dann in diese richtung... sollte das noch nicht reichen, schaut man sich an, ob nicht irgendwo zu viel gemacht wird, falls doch, wird das aufgelöst und nur noch das gemacht was nötig ist (z.b. rufst du ständig die initialisierung vom DEC auf, was sehr viel kostet). im anschluss versucht man dann noch weitere optimierungen, z.b. sprünge (z. b. calls) zu optimieren, indem man die parameter und rückgabeparameter optimiert.
für das ganze braucht man noch keinen profiler, es reicht i.d.r. der gesunde menschenverstand... denn es lohnt sich was zu machen, bei funktionen/procedureen welche häufig durchlaufen werden, die einmaligen sachen, braucht man noch nicht mal anzufassen.
PS: wenn du wirklich so viel wert auf performance legst, solltest dir eine implementation in assembler überlegen
PPS: ausserdem solltest du vermeiden, ständig etwas zu machen, was du nur einmal machen brauchst (z. B. Stringupcase). dies ist auch ein nettes beispiel, was man sich schnell inline z.b. in assembler codieren kann
Delete - Di 22.07.08 21:21
walter_b hat folgendes geschrieben: |
Vielen Dank. Ich weiss, dass diese Funktion ein Problemfall ist, bin auch schon dabei, sie zu optimieren, auch wenn sie in der DEC steckt ;)
Aber das Problem ist, dass ich gerne 4 Mio in einer Sekunde erreichen würde. Was meinst du? Ist wohl bei weitem nicht erreichtbar, oder? |
tja, das denk ich nicht, dass DEC hier das problem ist, sondern eher dein umgang mit dieser routine..
es reicht doch vollkommen, sie einmal zu instanzieren und nur dann zu initialisieren wenn sich das Pwd geändert hast... in deinen code machst du das aber ständig... und auch immer mit den gleichen parametern... dass sich das auf die performance niederschlägt, ist mehr als logisch...
walter_b - Di 22.07.08 21:32
Grenzgaenger hat folgendes geschrieben: |
walter_b hat folgendes geschrieben: | Vielen Dank. Ich weiss, dass diese Funktion ein Problemfall ist, bin auch schon dabei, sie zu optimieren, auch wenn sie in der DEC steckt ;)
Aber das Problem ist, dass ich gerne 4 Mio in einer Sekunde erreichen würde. Was meinst du? Ist wohl bei weitem nicht erreichtbar, oder? |
tja, das denk ich nicht, dass DEC hier das problem ist, sondern eher dein umgang mit dieser routine..
es reicht doch vollkommen, sie einmal zu instanzieren und nur dann zu initialisieren wenn sich das Pwd geändert hast... in deinen code machst du das aber ständig... und auch immer mit den gleichen parametern... dass sich das auf die performance niederschlägt, ist mehr als logisch... |
Das Problem ist, dass der erste Teil des Programmes alle Möglichkeiten durchgehen muss, und gleichzeitig müssen alle diese Möglichkeiten verschlüsselt werden (=> Ziel des Programmes) und deshalb bin ich doch fast dazu genötigt, diese Funktion ständig aufzurufen!?
PS:
Grenzgaenger hat folgendes geschrieben: |
PS: wenn du wirklich so viel wert auf performance legst, solltest dir eine implementation in assembler überlegen |
Ich kenne mich damit nicht aus. Wie genau mache ich das und was bringt es mir?
Hidden - Di 22.07.08 21:56
Hi,
Ist es nicht möglich, die Funktion als inline zu deklarieren(:?:) oder(mein Favourit) schleifenbasiert an der Stelle zu platzieren?
mfG,
walter_b - Di 22.07.08 22:05
Hidden hat folgendes geschrieben: |
Hi,
Ist es nicht möglich, die Funktion als inline zu deklarieren(:?:) oder(mein Favourit) schleifenbasiert an der Stelle zu platzieren?
mfG, |
Hm... mit inline meinst du, die Funktion direkt in das Programm einzubauen? Falls du dies meinst: ich arbeite daran, aber es sind einige hundert Zeilen, die dafür benötigt werden ;)
Oder wie meinst du schleifenbasiert? Wie genau würdest du dies umsetzen?
Lossy eX - Mi 23.07.08 09:55
Inline ist ein Zusatz bei einigen Methoden. Bei diesen Methoden darf der Kompiler den Inhalt des Codes an die Stelle des Aufrufes packen. Also du hast eine Methode die irgendwas macht und eine Stelle an der du sie aufrufst. Bei Inline wird dann nicht mehr die Methode aufgerufen sondern der Code der Methode an die Stelle des aufrufes kopiert. Ergebniss ist ein größerer Code aber weniger Sprunganweisungen. Bei wirklich kritischen stellen ist es dann schneller. Und ich denke Hidden meint mit Schleifen in etwa das Gleiche nur per Hand gemacht.
Assembler: Also komplett auf Assebler zu setzen halte ich für keine gute Idee. Besonders als Einsteiger fehlt da auch schlicht Hintergrundwissen bzw Wissen wie die CPU aufgebaut ist. Und so etwas ist fundamental wichtig bei Assembler sonst zerschießt du dir im warsten sinne des Worten willenlos irgendwelchen Speicher deiner Anwendnung. Außerdem ist Assembler keine Garantie dafür, dass es schnell wird.
Ich denke Grenzgaenger hat in sofern schon recht, dass du dein Programm erst mal schreiben und es später optimieren solltest. Nichts destro trotz ist eine sinnvolle Struktur von Anfang an wichtig. Denn wenn du ein bisschen auf das achtest was dein Code macht, dann wird kein Einsatz von Assembler nötig sein. Aber du darfst natürlich keine Sachen X Mal machen die nur ein mal benötigt werden.
Und wenn du mich fragst, dann solltest du die Delphistrings komplett sein lassen. Die Crypthbibliothek die ich kenne die arbeitet ausschließlich nur mit festen Blöckgrößen. Das einzige was keine feste Größe hat ist der Anfangsschlüssel. Aber das ist sonst auch nur ein Speicherbereich. Dieser wird nur zum Initialisieren eines Feldes benutzt. Und dieses Feld wird dann zum ver und entschlüsseln benutzt. Das wird aber auch jedes mal mit übergeben. Selbst die Datenblöcke haben eine Feste größe. Egal ob sie komplett oder nur halb gefüllt sind. Aber so hat man immer die gleichen Vorraussetzungen und kann die Codierung extrem schnell gestaltet. Das einzige was die beim ver oder entschlüsseln machen müssen ist das schlüsseln an sich. Alles andere muss in der Vorbereitung erledigt werden.
walter_b - Mi 23.07.08 14:09
Lossy eX hat folgendes geschrieben: |
Und wenn du mich fragst, dann solltest du die Delphistrings komplett sein lassen. Die Crypthbibliothek die ich kenne die arbeitet ausschließlich nur mit festen Blöckgrößen. Das einzige was keine feste Größe hat ist der Anfangsschlüssel. Aber das ist sonst auch nur ein Speicherbereich. Dieser wird nur zum Initialisieren eines Feldes benutzt. Und dieses Feld wird dann zum ver und entschlüsseln benutzt. Das wird aber auch jedes mal mit übergeben. Selbst die Datenblöcke haben eine Feste größe. Egal ob sie komplett oder nur halb gefüllt sind. Aber so hat man immer die gleichen Vorraussetzungen und kann die Codierung extrem schnell gestaltet. Das einzige was die beim ver oder entschlüsseln machen müssen ist das schlüsseln an sich. Alles andere muss in der Vorbereitung erledigt werden. |
Wenn du es nicht als String definieren würdest, was wäre dann dein Definitionsvorschlag? Array?
Lossy eX - Mi 23.07.08 16:10
Ja ich meinte schon irgendwelche festen Strukturen. Denn diese Stringaddition (string = string + string) von Delphi ist zwar ganz schön, nett und kompfortable. Aber das ist Gift für Sachen bei denen es wirklich auf Geschwindigkeit ankommt. Zu mal die Freigabe der String aus automatisiert passiert. Und das mit einem unsichtbaren Try Finally Block. Was auch gut Zeit fressen kann. Feste Strukturen als Variablen liegen normal auf dem Stack und die zu Erstellen bzw. Freizugeben kostet im Vergleich so gut wie nichts.
Wie man das genau definieren kann hängt natürlich davon ab wie dein Algorithmuss arbeitet bzw was du alles damit anstellen willst etc etc etc. Aber kleines Beispiel.
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| TKey = array [0..15] of byte; TBlock = array [0..63] of byte;
InitializeKey(KeyString: String; var Key: TKey);
Encode(const Key: TKey; var Block: TBlock); |
InitializeKey würde deinen String in einen Key verwandeln und den TKey befüllen.
Encode würde die Daten in Block mit dem Key transformieren. Dadurch, dass dein Block immer ein Vielfaches von dem Key ist, brauchst du eigentlich auch kaum viel in der Methode berücksichtigen. Sondern sie muss/sollte lediglich die Daten im Block nur mit dem Schlüssel verrechnen.
Aber das bitte nicht überbewerten. Ich weiß nicht was du mit deinem Code vor hast und die größen des Keys/Blocks müssten wohl auch angepasst werden. Nur so hättest du keine Strings sondern nur pure Daten und der Code den du auf den Block anwenden würdest wäre für alles selbst verantortlich.
Du musst beim Befüllen eines Blockes aber immer mit reinschreiben wie groß der eigentliche Inhalt ist. Bzw bei Inhalten dessen Größe variieren kann. Da du sonst beim Dekodieren nicht weißt wie groß deine Daten wirklich sind.
walter_b - Do 24.07.08 13:27
Habe das ganze jetzt mal umgebaut, dass es mit einem Array of Byte funktioniert. Allerdings ist jetzt eine andere Frage bei mir aufgetaucht. Die eine Funktion lautet ja so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function DecodeText(Inputtext, Key: String): String; begin with TCipher_1DES.Create do try Mode := cmECBx; Init(Key); Result := EncodeBinary(Inputtext, TFormat_HEXL); finally Free; end; end; |
Und die wird 4 Mio mal aufgerufen, da sich "Key" jedes mal ändert. Eigentlich ist es sinnlos, "with TCipher_1DES.Create do" dann jedes mal wieder neu aufzurufen, oder meine ich das nur? Jedoch wenn ich es so umbaue, dass ich es direkt in die Prozedur, welche alle Möglichkeiten durchgeht, implementiere, sodass "with TCipher_1DES.Create do" am Anfang, danach die Prozedur und dann der restliche Teil von DecodeText steht, sodass jedes mal eigentlich nur "Init(Key) und "EncodeBinary) aufgerufen wird, meldet er mir "Undeclared identifier: Init". Wieso funktioniert das dort nicht mehr? Vorher klappte es ja auch!?
Edit: Wenn ich die ganze Deklaration vorne ranhänge (TDECCipher.Init(Key); ) schlägt er mir "This form of method call only only allowed for class methods". Was will er mir hier genau erzählen? Was mache ich falsch?
Hidden - Do 24.07.08 13:56
Hi,
So, wie du es aufrufst, wäre das keine Methode des Objekts, sondern der Klasse.
'.Create' ist eine Klassenmethode(und hat ein Objekt als Rückgabewert). TMyClass.Create.DoSomething wäre eine Methode des Objekts, das von Create zurückgegeben wird.
Umgekehrt ist .Create aber auch nur eine Klassenmethode, deshalb funktioniert z.B. MyInstance.Create nicht, sondern nur myInstance := TMyClass.Create.
Warum der obere Aufruf invalid ist, weiß ich nicht, sofern TCipher_1DES eine Methode namens Init hat.. Esseidenn, es gäbe eine klassenlose Methode namens Init, die Stattdessen aufgerufen werden würde.
Probier mal aUnit.Init(Key) .. k.A., ob das geht, hab gerad kein Delphi da. Damit würde man ja normalerweise eine Methoden-Verwechslung ausschließen(z.B. Windows.Beep statt System.Beep). Ob das nun aber mit dem With-Statement inteferriert, kann ich nicht sagen.
Eventuell einfach die Methode umbenennen. Klappt es denn, wenn du das With-Statement ausbaust und die Erzeugte Instanz in einer Variable abspeicherst?
Naja.. Bleibt halt Fehlerquelle ersten Ranges, das With-Statemant :P
mfG,
walter_b - Do 24.07.08 14:10
Hm... funktioniert irgendwie nicht... Die Funktion ist übrigens so definiert:
procedure TDECCipher.Init(Key: Array of Byte; IFiller: Byte);, während TDECCipher wiederrum so definiert ist:
TDECCipher = class(TDECObject)
Noch was am Rande: Delphi bringt mich echt noch zum ausrasten! Meine gekürzte Version:
Delphi-Quelltext
1: 2: 3:
| begin DoEncode(@S[0], @D[0], 8); end; |
Die lange Version:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| begin if Context.BlockSize = 1 then begin DoEncode(S, D, 8); FState := csEncode; end else begin Dec(Size, 8); I := 0; while I <= Size do begin DoEncode(@S[I], @D[I], 8); Inc(I, 8); end; Dec(Size, I - 8); if Size > 0 then if Size mod Context.BlockSize = 0 then begin DoEncode(@S[I], @D[I], Size); FState := csEncode; end; end; end; |
Dabei habe ich nur die Sachen gestrichen, die aufgrund des If sowieso nicht aufgeruft werden, und da das while nur einmal durchgeführt wird, habe ich dies auch gleich entfernt. Als Resultat kommt das selbe raus, nur ist meine Version natürlich wieder einmal viel langsamer. Ich weine bald, wenn es so weitergeht. :cry:
Edit2: Mir gibts gleich was. Die Assertfunktion dient ja dem Programmabbruch, falls irgendetwas nicht true sein sollte. Ich bin ganz sicher, dass es bei mir immer true ist und habe die Funktion deshalb entfernt, natürlich wieder einmal mit dem normalen Ergebnis, dass alles langsamer wurde...
alzaimar - Do 24.07.08 14:42
Wieso nicht
??
walter_b - Do 24.07.08 14:59
alzaimar hat folgendes geschrieben: |
Wieso nicht
?? |
Der Teil fällt weg, da Context.BlockSize nicht 1 ist.
alzaimar - Do 24.07.08 15:08
Sollte aber das Gleiche sein. Der Compiler erzeugt aus dem '@X[0]' ein
Quelltext
1: 2: 3: 4:
| "Nimm Adresse von X" "Addiere 0" "Das ist ein Wert" "Davon die Adresse" |
Und aus "X"
Quelltext
1:
| "Nimm die Adresse von X" |
Irgendwie kürzer. Probiere es einfach.
walter_b - Do 24.07.08 15:26
alzaimar hat folgendes geschrieben: |
Sollte aber das Gleiche sein. Der Compiler erzeugt aus dem '@X[0]' ein
Quelltext 1: 2: 3: 4:
| "Nimm Adresse von X" "Addiere 0" "Das ist ein Wert" "Davon die Adresse" |
Und aus "X"
Quelltext 1:
| "Nimm die Adresse von X" |
Irgendwie kürzer. Probiere es einfach. |
Nein, funktioniert nicht. Folglich muss wohl auf die 0. Stelle referenziert werden.
Lossy eX - Do 24.07.08 15:28
Also ich muss zu meiner Schande gestehen, dass mir nicht aufgefallen ist, dass du eine externe Bibliothek benutzt die bereits alles beinhält. Ich bin bisher davon ausgegangen, dass du selber eine Verschlüsselung auf die Beine stellen willst! Das habe ich gerade gemerkt als ich mir mal den Code angeschaut hatte. Du solltest natürlich an einer bestehenden Bibliothek nichts verändern. Außer es ist absolut notwendig und du kennst dich damit entsprechend gut aus.
Und eigentlich geht es doch nur um die Verwendung der Klasse TDECCipher, oder? Normal sieht es so aus, dass du davon eine Instanz erstellst. Diese einem Schlüssel zuweist und anschließend deine gesammte Daten ver/entschlüsseln lässt. Normal ist das Ver/Entschlüsseln recht schnell aber das Initialisieren etwas langsamer. Allerdings so wie du das in deiner Methode machst müsste das Prinzipiel schon richtig sein. Nur solltest du da nicht dauerhaft die Instanz der Klasse wegschmeißen. Denn dann musst du für jeden kleinen Text den Schlüssen neu initialieren lassen.
Also in etwa so.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| Chipher := TDECCipher.Create; Cipher.Mode := cmECBx; Cipher.Init(Key);
Cipher.EncodeBinary(...); Cipher.EncodeBinary(...); Cipher.EncodeBinary(...); Cipher.EncodeBinary(...);
Cipher.DecodeBinary(...); Cipher.DecodeBinary(...); Cipher.DecodeBinary(...); Cipher.DecodeBinary(...);
Chipher.Free; |
Wenn sich der Schlüssel dauerhaft ändert, dann wirst du da vermutlich keine große andere Chance haben als wie du es schon gemacht hattest. Allerdings frage ich mich worin da der Sinn besteht? Denn wie gesagt. Das Initialisieren des Schlüssels dauert normal seine Zeit. Das ist einfach so. Und so wäre es auch nicht möglich das volle Potential der Verschlüsselung ausnutzen zu können.
walter_b - Do 24.07.08 15:45
Dass man an der Datenbank eigentlich nichts verändern soll, erschien für mich auch logisch, allerdings konnte ich die Finger nicht davon lassen :D Da die Datenbank auf den allgemeinen Fall ausgelegt ist, sollte man dort auch ein bisschen Optimieren können. Ist mir bisher auch ein wenig gelungen ;)
Bei der Verschlüsselung, die ich verwenden will, ist es leider so, dass der Key ständig wechselt und immer der selbe Text verschlüsselt wird. Deshalb ist deines wohl in diesem Falle nicht des Rätsels Lösung (tut mir leid, dich enttäuschen zu müssen :D).
Da ich sonst keine grosse Auswahl mit dem Optimieren mehr habe, habe ich mir gedacht, dass das with-Element etwas überflüssig ist, da jedes mal, wenn ich die Funktion aufrufe, wieder das Create neu lanciert wird. Deshalb wollte ich es wie folgt machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| procedure Gen(); var X, Y, I, Len, p: Integer; Wort: arr8; Key: arr8; begin with TCipher_1DES.Create do Len := 0; for X := 0 to 7 do wort[X] := (Ord('A')-65);
inc(wort[0],32); for X := 0 to combs do begin Y := 0; inc(wort[Y]);
while wort[Y] > Ord('z') do begin if (X = 90) or (X = 8190) or (X = 737190) or (X = 66347190) or (X = 5971247190) or (X = 537412247190) then inc(wort[Y + 1],32);
inc(wort[Y + 1]); wort[Y] := ord('!'); inc(Y); Len := max(Y, Len); end; anf:=parityshift(Wort); try Mode := cmECBx; TDECCipher.Init(anf); Result := EncodeBinary('KGS!@#$%', TFormat_HEXL); finally Free; end; end; end; |
Hier würde nur einmal das with-Element aufgerufen werden (auch wenn ich keine Ahnung habe, wofür dies überhaupt gut ist), und anschliessend würde nur noch der Key ( per Initfunktion) verändert und verschlüsselt werden. Allerdings meldet er, wie bereits angesprochen, immer folgendes:
Delphi hat folgendes geschrieben: |
"This form of method call only only allowed for class methods" |
Worauf will er mich aufmerksam machen? Bzw. was muss ich ändern?
Die Funktion ist wie folgt definiert:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| TDECCipherClass = class of TDECCipher; TDECCipher = class(TDECObject) private FData: PByteArray; FDataSize: Integer; protected FBufferIndex: Integer; FUserSize: Integer; FBuffer: PByteArray; FVector: PByteArray; FFeedback: PByteArray; FUser: Pointer; FUserSave: Pointer; procedure DoInit(Key: Array of Byte); virtual; abstract; procedure DoEncode(Source, Dest: Pointer); virtual; abstract; public constructor Create; override; class function Context: TCipherContext; virtual; abstract; procedure Init(Key: Array of Byte; IFiller: Byte = $FF); procedure Protect; virtual; procedure Encode(const Source; var Dest); function EncodeBinary(): Binary; published end; |
Allesquarks - Do 24.07.08 16:03
TDECCipherclass ist eine Metaklasse oder Klassenreferenz. Dies ist ein Zeiger sozusagen auf die abstrakte Klassendefinition, welche keine Instanz benötigt. Alle constructoren fallen normalerweise darunter, weil eben diese ja nicht von einer Instanz aufgerufen werden, sondern diese erst bauen sollen.
normalerweise würde man das einfach so schreiben: DECCipherinstance.init(). In deinem fall mit with kannst du auch einfach init() schreiben, da im with alle Methoden versucht werden auf die with Instanz zu beziehen.
Was du vlt noch optimieren könntest wäre das modulo. Das dürfte selbst in deinem Fall mit sehr kleinen Zahlen noch verhältnismäßig langsam sein.
Am meisten optimieren kann man immer durch einen ganzheitlichen Ansatz anstatt darüber zu philosophieren ob man pchar oder doch lieber array of byte verwendet. Du hast zwar zu einem Teil schon im andern Thread erklärt, aber nicht überaus ausführlich. Ich denke es würde helfen, wenn du einmal bis ins letzte Detail erklärst was du machen möchtest (wozu du überhaupt Schlüssel baust nach welchem Algorithmus du vorgehst also welche Funktion die einzelnen Module haben bisher weiß ich nur alles zusammen soll dann mal ein LM-Hash sein, dne zumindest ich nicht kenne) Was willst du erhalten Passworts oder keys sowie womit du was verschlüsselst und vor allem welche Teile des Codes hast du als Open-Source und darfst du verändern.
walter_b - Do 24.07.08 16:21
Allesquarks hat folgendes geschrieben: |
man das einfach so schreiben: DECCipherinstance.init(). In deinem fall mit with kannst du auch einfach init() schreiben, da im with alle Methoden versucht werden auf die with Instanz zu beziehen. |
Schreibe ich nur Init(anf), dann steht dort: Undeclared identifier: Init. Was mache ich falsch?
Allesquarks hat folgendes geschrieben: |
Was du vlt noch optimieren könntest wäre das modulo. Das dürfte selbst in deinem Fall mit sehr kleinen Zahlen noch verhältnismäßig langsam sein. |
modulo? Wo habe ich ein Modulo? Meinst du in den HexStrToString und StringToHexStr? Falls ja, dann kann ich dich beruhigen: Diese Funktion wurde abgeschafft, war nur für mich überraschend, dass er damit schneller war als ohne.
Allesquarks hat folgendes geschrieben: |
Am meisten optimieren kann man immer durch einen ganzheitlichen Ansatz anstatt darüber zu philosophieren ob man pchar oder doch lieber array of byte verwendet. Du hast zwar zu einem Teil schon im andern Thread erklärt, aber nicht überaus ausführlich. Ich denke es würde helfen, wenn du einmal bis ins letzte Detail erklärst was du machen möchtest (wozu du überhaupt Schlüssel baust nach welchem Algorithmus du vorgehst also welche Funktion die einzelnen Module haben bisher weiß ich nur alles zusammen soll dann mal ein LM-Hash sein, dne zumindest ich nicht kenne) |
Okee, ich erklär es möglichst einfach: Ich versuche John the Ripper in einfachster Form nachzubauen. Und zwar gehe ich davon aus, dass ich einen gegebenen LM-Hash habe (LM-Hash wird in der SAM-Datei auf dem PC verwendet, um die Passwörter für die Benutzeraccounts zu speichern.). JTR benötigt ebenfalls den LM-Hash, um das Passwort zu finden. Sein Vorgehen ist folgendes: Er geht alle Kombinationen durch und erstellt während dem Vorgang aus jeder Kombination einen LM-Hash und vergleicht, ob dieser mit dem gegebenen Hash überein stimmt. (Tatsächlich funktioniert er anders, aber dies ist meine Version, wie ich das gerne umsetzen will und wie es auch einfach zu verstehen ist).
Der LM-Hash funktioniert folgendermassen: Zuerst wird ein Key erstellt (dazu wird parityshift und DecodeText benötigt) und anschliessend wird ein fester String (bei LM-Hash ist dies 'KGS!@#$%') mit dem erstellten Key verschlüsselt. Aus diesem Grund muss ich aus jeder Kombination zuerst den Key erstellen und anschliessend noch verschlüsseln.
Natürlich mache ich das Programm nicht einfach so aus langeweile oder weil ich mich kriminell betätigen will, sondern das ganze geschieht im Dienste der Wissenschaft (bei Interesse PN an mich).
Hidden - Do 24.07.08 16:48
Hi,
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| procedure Gen(); var X, Y, I, Len, p: Integer; Wort: arr8; Key: arr8; begin with TCipher_1DES.Create do begin Len := 0; for X := 0 to 7 do wort[X] := (Ord('A')-65);
inc(wort[0],32); for X := 0 to combs do begin Y := 0; inc(wort[Y]);
while wort[Y] > Ord('z') do begin if (X = 90) or (X = 8190) or (X = 737190) or (X = 66347190) or (X = 5971247190) or (X = 537412247190) then inc(wort[Y + 1],32);
inc(wort[Y + 1]); wort[Y] := ord('!'); inc(Y); Len := max(Y, Len); end; anf:=parityshift(Wort); try Mode := cmECBx; TDECCipher.Init(anf); Result := EncodeBinary('KGS!@#$%', TFormat_HEXL); finally Free; end; end; end; |
mfG,
Allesquarks - Do 24.07.08 16:57
Ah siehst du ich dachte du willst dann wirklich einen größeren Text verschlüsseln. Mir ist egal was du damit machst. Vielleicht hast du auch nur dein eigenes Passwort vergessen.
Ok ich versteh gar nichts vom LM, aber wenn ich das richtig verstehe hast du nach der Hälfte einen key mit dem dann codiert/decodiert wird. Falls der immer eine feste länge hat könnte man überlegen den nicht als string oder pchar sondern einfach als "speicher" zu übergeben. Falls du den Quelltext für die gesamte Hash-Funktion hast würde ich dir empfehlen da Teile umzuschreiben. Gar nicht vom Algorithmus her sondern von den Typen.
1.DecodeText gibt string zurück. Vlt kannst du erreichen, dass das Ding ein Pointer auf array of byte oder pchar zurückgibt.
2. Für praityshift gilt glaube ich das gleiche.
3. Hast du vlt schon umgesetzt: den constructor wenn irgend möglich außerhalb einer jeden Schleife aufrufen. Generell ist jede Form von dynamischer Speicherallokation zu vermeiden.
4. In deinen ganzen or's schalte mal per Compiler-Schalter die kurze Boolsche Auswertung ein und mach das unwahrscheinlichste Kriterium nach vorne.
Zum init() keine Ahnung was da nicht passt aber lass doch einfach das with weg. Ist sowieso nur für den Compiler und sollte theoretisch keine Wirkung haben. Wenn eine da ist vermute ich aber eher eine nachteilige.
walter_b - Do 24.07.08 17:00
:oops: :oops: Vielen Dank! Jetzt muss ich einfach das Free entfernen, da er mir sonst die Schleife zerstört.
Allesquarks hat folgendes geschrieben: |
1.DecodeText gibt string zurück. Vlt kannst du erreichen, dass das Ding ein Pointer auf array of byte oder pchar zurückgibt.
2. Für praityshift gilt glaube ich das gleiche. |
Hm... Irgendwie sollte das schon zu erreichen sein. Bei Parityshift habe ich ja im Moment die Funktion mit einem Array of Byte, und am Schluss steht da noch Result:=*Name des Arrays*. Wenn ich hier jetzt einfach einen Pointer als Resultat zurückgebe, welcher auf den Array zeigt, wäre dann die Aufgabe schon erfüllt oder müsste das ganze mit dem Pointer arbeiten?
Allesquarks hat folgendes geschrieben: |
4. In deinen ganzen or's schalte mal per Compiler-Schalter die kurze Boolsche Auswertung ein und mach das unwahrscheinlichste Kriterium nach vorne. |
Du meinst wahrscheinlich die Or's in Parityshift, oder? Beide Werte, die da "geord" werden, sind jeweils Byte. Ich dachte immer, da wird dann die Zahl im Binärcode verglichen und die Einsen und Nullen verändert, je nach dem, wie die Konstellation ist, nicht? Oder sind das keine "Logical (bitwise) operators"?
Allesquarks - Do 24.07.08 17:25
Kurze Boolsche Auswertung heißt bsp:
Delphi-Quelltext
1:
| if (a=3) and (b=4) and (c=5) |
, dass er schon aufhört wenn a<>3 ist, da das Ding dann insgesamt nicht mehr true werden kann.
Bei dir werden über die = mehrere logische Werte angelegt. Intern sind das immer noch bytes (kleinste Speichereinheit), aber das interessiert dabei eher weniger.
Außerdem würde ich noch das try except enfernen. Da doch eigentlich immer ein gültiger Schlüssel übergeben wird sollte auch kein Stuss rauskommen den man abfangen sollte.
Diesen Generaltext, den du hardgecoded da drin hast würde ich wenn möglich auch initial am besten als constant anlegen und dann als Zeiger übergeben.
Mach mal einen Breakpoint bei der max function und mach das CPU-Window auf und guck ob da irgendwas von call steht. Wenn ja dann sag Bescheid.
Generell würde ich dem Delphi-Compiler so wenig wie möglich selbst überlassen.
walter_b - Do 24.07.08 17:37
Allesquarks hat folgendes geschrieben: |
Kurze Boolsche Auswertung heißt bsp:
Delphi-Quelltext 1:
| if (a=3) and (b=4) and (c=5) | , dass er schon aufhört wenn a<>3 ist, da das Ding dann insgesamt nicht mehr true werden kann.
Bei dir werden über die = mehrere logische Werte angelegt. Intern sind das immer noch bytes (kleinste Speichereinheit), aber das interessiert dabei eher weniger. |
Ach so meintest du. Okee, werde ich machen.
Allesquarks hat folgendes geschrieben: |
Diesen Generaltext, den du hardgecoded da drin hast würde ich wenn möglich auch initial am besten als constant anlegen und dann als Zeiger übergeben. |
Diesen Text habe ich in einer Duplikatversion mal direkt in die Datenbank ein bisschen weiter reinverpflanzt, und so war es schon mal schneller als ursprünglich. Aber werde auch noch den Vergleich mit dem Zeiger anstellen, danke für den Hinweis.
Allesquarks hat folgendes geschrieben: |
Mach mal einen Breakpoint bei der max function und mach das CPU-Window auf und guck ob da irgendwas von call steht. Wenn ja dann sag Bescheid. |
Jep, er ruft den Max an. Was bedeutet jetzt das? Ist das Positiv oder Negativ?
Zitat: |
move edx [ebp-$08]
move eax,ebx
call max |
Allesquarks - Do 24.07.08 18:09
Probier mal das vor allem mit inline und guck mal erneut nach dem call. Falls er es nicht inlined ists vlt ja auch so schneller, obwohl die max aus math sehr ähnlich sein dürfte. Vor allem könnte es auch einen Unterschied machen wierum du die beiden Zahlen übergibts => ausprobieren.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| function _max(Zahl1,Zahl2:integer):integer;inline; asm cmp eax,edx; jb @@switch; ret; @@switch: xchg eax,edx; end; |
Edit: VLT lässt sich diese Version besser inlinen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| function _max(Zahl1,Zahl2:integer):integer;inline; asm cmp eax,edx; jge @@done; xchg eax,edx; @@done: end; |
walter_b - Do 24.07.08 18:17
Allesquarks hat folgendes geschrieben: |
Probier mal das vor allem mit inline und guck mal erneut nach dem call. Falls er es nicht inlined ists vlt ja auch so schneller, obwohl die max aus math sehr ähnlich sein dürfte. Vor allem könnte es auch einen Unterschied machen wierum du die beiden Zahlen übergibts => ausprobieren.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| function _max(Zahl1,Zahl2:integer):integer;inline; asm cmp eax,edx; jb @@switch; ret; @@switch: xchg eax,edx; end; | |
Declaration expected but 'INLINE' found.
Noch was anderes. Ich bin nicht sicher, ob du den Sinn von "max" im Programmcode verstanden hast. Die Funktion max() wird für das Programm überhaupt
NICHT benötigt. Doch während meinen Leistungstests hat sich gezeigt, dass das ganze Programm einiges schneller ist, wenn diese Funktion darin vorhanden ist (auch wenn sie keinen Einfluss aufs Programm hat). Von da her müsstest du nicht versuchen, sie zu optimieren. ;) Falls du auf der Suche bist, warum es bei mir dadurch schneller ist, dann hab ich nix gesagt ;)
Delete - Do 24.07.08 21:01
ein ASM code lässt sich nicht inlinen, das ist eine der einschränkungen.
PS: Walter, mein tipp ist immer noch, schau doch erst mal, dass das programm rund läuft, bevor du dich auf die performance optimierung einlässt.
walter_b - Do 24.07.08 21:41
Grenzgaenger hat folgendes geschrieben: |
PS: Walter, mein tipp ist immer noch, schau doch erst mal, dass das programm rund läuft, bevor du dich auf die performance optimierung einlässt. |
Das Programm läuft schon lange. Wäre eigentlich fertig. Alles was noch fehlt ist die Performance. Und die Probleme, wegen welchen ich hier immer mal wieder nach Hilfe schreie, tauchen meistens bei irgend einer Optimierung oder sonst etwas auf.
Edit: Ich habe gefunden, welche Prozedur mir praktisch die ganze Zeit frisst (in etwa 23 Sekunden der benötigten 24):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50:
| procedure TCipher_1DES.DoInitKey(const Data: array of Byte; Key: PLongArray; Reverse: Boolean); const ROT: array[0..15] of Byte = (1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28); var I,J,L,M,N: LongWord; PC_M,PC_R: array[0..55] of Byte; K: array[0..31] of LongWord; begin FillChar(K, SizeOf(K), 0); for I := 0 to 55 do if Data[DES_PC1[I] shr 3] and ($80 shr (DES_PC1[I] and $07)) <> 0 then PC_M[I] := 1 else PC_M[I] := 0; for I := 0 to 15 do begin if Reverse then M := (15 - I) shl 1 else M := I shl 1; N := M + 1; for J := 0 to 27 do begin L := J + ROT[I]; if L < 28 then PC_R[J] := PC_M[L] else PC_R[J] := PC_M[L - 28]; end; for J := 28 to 55 do begin L := J + ROT[I]; if L < 56 then PC_R[J] := PC_M[L] else PC_R[J] := PC_M[L - 28]; end; L := $1000000; for J := 0 to 23 do begin L := L shr 1; if PC_R[DES_PC2[J ]] <> 0 then K[M] := K[M] or L; if PC_R[DES_PC2[J + 24]] <> 0 then K[N] := K[N] or L; end; end; for I := 0 to 15 do begin M := I shl 1; N := M + 1; Key[0] := K[M] and $00FC0000 shl 6 or K[M] and $00000FC0 shl 10 or K[N] and $00FC0000 shr 10 or K[N] and $00000FC0 shr 6; Key[1] := K[M] and $0003F000 shl 12 or K[M] and $0000003F shl 16 or K[N] and $0003F000 shr 4 or K[N] and $0000003F; Key := @Key[2]; end; end; |
Und zwar ist es der gehighlightete Teil, welcher besonders zeitfressend ist. Und da diese Funktion aus der Datenbank ist und zweimal aufgerufen wird (einmal Reverse mit True und einmal mit False), denke ich nicht, dass dies noch gross optimierbar ist, da für Data und für Key immer wieder andere Werte kommen. So wies aussieht, ist das Projekt gescheitert :cry: Falls jemand noch eine Möglichkeit sieht, dies hier zu optimieren, darf er sie gerne anbringen.
Ein herzliches Dankeschön an alle, welche mich hier während dem Projekt mit ihren Tipps, Ratschlägen oder Demonstrationen unterstützt haben.
Delete - Fr 25.07.08 18:28
ich wiederhole mich nur ungern, aber du solltest wirklich an deiner logik feilen, anstatt nur isoliert einzelne befehle zu betrachten.
ist schon klar, dass das markierte mehr zeit frist, als der restliche teil, da er sich ja in einer schleife befindet und ausserdem wieder schleifen enthält.
wie gesagt, war mein tipp hier vor ewigkeiten dass du versuchst, nur einmal die klasse aufzurufen und zu initialisieren. ansonsten solltest du versuchen, die routinen auseinanderzunehmen und die ergebnisse zwischenzuspeichern, damit du sie bei bedarf neu zusammenfügen kannst, ohne die daten neu zu berechnen.
Lossy eX - Fr 25.07.08 18:53
Der Punkt ist doch einfach, dass du etwas machst was typischerweise nicht das erklärte Ziel einer solchen Verschlüsselung ist. Bist du schon mal auf die Idee gekommen, dass der Algorithmuss auch genau solche Sachen verhindern soll???
Und hier kommt eben genau zum Tragen was ich gesagt habe. Die Erstellung des Schlüssels ist sehr kostspielig, da die Ver und Entschlüsselung nun mal sehr schnell sein muss. Da typischerweise eben mehr Daten verschlüsselt werden als Schlüssel verdatet. Die Methode in Assambler umzuwandeln würde sicher etwas bringen. Von 23 Sekunden wirst du den Code auf 22,95 beschleunigen können. Der Code arbeitet nur mit fixen Strukturen etc. Das ist so aufgebaut, dass da kaum etwas zu machen ist. Der ist deswegen so langsam, da er nun mal sehr viel machen muss.
Das einzige wodurch du evtl etwas machen könntest, was auch nur ansatzweise sinnvoll wäre, wären Threads. Also auf Rechnern mit mehr als einem Prozessor dann mehr Berechnungen gleichzeitig durchführen. Alles andere wird vermutlich den Zeitaufwand nicht gerecht werden.
GTA-Place - Fr 25.07.08 19:32
Grenzgaenger hat folgendes geschrieben: |
ein ASM code lässt sich nicht inlinen, das ist eine der einschränkungen. |
Abgesehen davon: Inline gibts glaub in D6 und D7 noch gar nicht, oder täusch ich mich?
Tilman - Fr 25.07.08 19:38
GTA-Place hat folgendes geschrieben: |
Abgesehen davon: Inline gibts glaub in D6 und D7 noch gar nicht, oder täusch ich mich? |
Was macht denn inline überhaupt? In meiner D7-Hilfe steht "inline wird nur noch wegen der Abwärtskompatiblität beibehalten, und hat keinerlei auswirkung auf den Compiler".
walter_b - Fr 25.07.08 19:46
Grenzgaenger hat folgendes geschrieben: |
ich wiederhole mich nur ungern, aber du solltest wirklich an deiner logik feilen, anstatt nur isoliert einzelne befehle zu betrachten.
ist schon klar, dass das markierte mehr zeit frist, als der restliche teil, da er sich ja in einer schleife befindet und ausserdem wieder schleifen enthält.
wie gesagt, war mein tipp hier vor ewigkeiten dass du versuchst, nur einmal die klasse aufzurufen und zu initialisieren. ansonsten solltest du versuchen, die routinen auseinanderzunehmen und die ergebnisse zwischenzuspeichern, damit du sie bei bedarf neu zusammenfügen kannst, ohne die daten neu zu berechnen. |
Es ist mir durchaus klar, was du sagst. Aber das Ziel meiner Arbeit ist es, ein schnelles Programm und kein schönes zu haben. Das Programm war ursprünglich mal über 300 Sekunden langsam, und jetzt ist es auf 24. Ich habe die Sturkturen mal für mal durchgeschaut und wieder nach Verbesserungen gesucht. Ich habe Pointer verwendet, Pointer aus Pascal-Strings, Pointer auf normale Strings, die Daten in Arrays, die Daten direkt in den Strings, alles habe ich durchprobiert. Doch dann bin ich dort angekommen, wo ich nicht mehr wirklich sah, wie ich es noch grossartig verbessern könnte. Ebenfalls zeigten die Zeittests, dass der Teil, welchen ich geschrieben habe, ungefähr eine Sekunde einnimmt. Wenn ich das jetzt noch durch einen riesigen Aufwand halbieren könnte, schön und gut, doch das Ziel wird trotzdem weit gefehlt. Deshalb habe ich weitergesucht, was mir effektiv die Zeit frisst, und da stiess ich auf die vorhin genannte Funktion. Mir ist klar, dass die Logik stimmen muss und man mit den optimalen Parametern arbeiten sollte und alles. Jedoch habe ich festgestellt, wie eben erläutert, dass wirklich nur diese Funktion, welche in der Verschlüsselungsdatenbank ist, mir praktisch alle Zeit frisst. Dann kann ich an meiner Logik noch rumbasteln wie ich will, die Funktion bleibt gleich aufwendig und von da her komme ich auch nicht an die gewünschte Zeit ran. Von da her betrachte ich es, tut mir leid, wenn ich dir zunahe trete und so sehr ich auch Wert auf deine Tipps lege, als unnötig, hier noch an der Logik oder sonst etwas rumzuspielen, wenn ich ja eigentlich weiss, wo der fette Brocken sitzt und ich mir auch darüber im klaren bin, dass ich das wahrscheinlich nicht ändern kann.
Lossy eX hat folgendes geschrieben: |
Der Punkt ist doch einfach, dass du etwas machst was typischerweise nicht das erklärte Ziel einer solchen Verschlüsselung ist. Bist du schon mal auf die Idee gekommen, dass der Algorithmuss auch genau solche Sachen verhindern soll??? |
Jep, genau daran habe ich auch gedacht, dass es nämlich absichtlich so gemacht wurde, da der Computer den Algorithmus nur einmal durchführen muss, und von da her könnte er sogar noch viel aufwendiger sein. Bemerkbar macht sich dies erst, wenn man es einige Male durchführen muss. Aber das ist wohl eine der Grundideen hinter dem Vorgang.
Lossy eX hat folgendes geschrieben: |
Und hier kommt eben genau zum Tragen was ich gesagt habe. Die Erstellung des Schlüssels ist sehr kostspielig, da die Ver und Entschlüsselung nun mal sehr schnell sein muss. Da typischerweise eben mehr Daten verschlüsselt werden als Schlüssel verdatet. Die Methode in Assambler umzuwandeln würde sicher etwas bringen. Von 23 Sekunden wirst du den Code auf 22,95 beschleunigen können. Der Code arbeitet nur mit fixen Strukturen etc. Das ist so aufgebaut, dass da kaum etwas zu machen ist. Der ist deswegen so langsam, da er nun mal sehr viel machen muss. |
Genau. Wie vorhin angesprochen, bringt es in meinen Augen nichts, einen minimalen Aufwand noch zu verringern, wenn man gegen den grossen chancenlos ist. Ob es 24 oder 23 Sekunden waren, interessiert am Ende dann niemanden mehr.
Lossy eX hat folgendes geschrieben: |
Das einzige wodurch du evtl etwas machen könntest, was auch nur ansatzweise sinnvoll wäre, wären Threads. Also auf Rechnern mit mehr als einem Prozessor dann mehr Berechnungen gleichzeitig durchführen. Alles andere wird vermutlich den Zeitaufwand nicht gerecht werden. |
An diesem PC hier habe ich einen Quad-Core. Heisst das, dass ich die Zeit auf 5-6 Sekunden reduzieren könnte? Wie müsste ich das genau anstellen?
Delete - Fr 25.07.08 19:46
GTA-Place hat folgendes geschrieben: |
Grenzgaenger hat folgendes geschrieben: | ein ASM code lässt sich nicht inlinen, das ist eine der einschränkungen. |
Abgesehen davon: Inline gibts glaub in D6 und D7 noch gar nicht, oder täusch ich mich? |
ist glaub ich 'n feature von D2006 ... ab da implementiert.
inline ist die delphi antwort auf 'n macro assembler bzw. auf macros. wo man den code einfach in routinen hineinkopiert, anstatt ihn aufzurufen. dadurch wird der code schneller, da er sich die ganzen overheads erspart.
Lossy eX - Sa 26.07.08 15:24
Inline gabs früher mal (meine pascalzeiten). Und in Delphi 7 war das nutz- und wirkungslos. Allerdings mit neuerem Delphis (meine auch 2006) hat es wieder eine Wirkung bekommen. Und zwar sieht das so aus, dass der Code innerhalb einer Methode anstelle des Aufrufes kopiert wird. Es fällt also ein Funktionsausfruf weg, da der Code direkt dorthin kopiert wird. Bei kleinen Methoden die sehr häufig aufgerufen werden sparrt man durchaus einiges an Zeit.
Multithreads: Ich weiß nicht ob ich persönlich so viel interesse habe da so viel energie in dein Problem zu stecken.
http://www.opengl24.de/index.php?cat=tutorials&file=multithreading
walter_b - Sa 26.07.08 18:15
Super, vielen Dank. Jetzt bin ich im Moment auf 16.5 Sekunden mit 4 Threads (wegen Quad-Core). Viel mehr geht wohl nicht mehr, oder?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!