Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - String in Datum umwandeln z.B. '01-Jan-2005' in Tdate


molendrotter - Fr 12.08.05 19:40
Titel: String in Datum umwandeln z.B. '01-Jan-2005' in Tdate
hi will einen speziellen String in Tdate format umwandeln:
Der string sieht so aus: '01-Aug-05' also dd-Mmm-yyyy wobei die Monate immer so aussehen: Jan Feb....

ich könnte den String mit StringReplace und if abfragen bestimmt in '01-08-05' umwandeln und dann das machen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  FormatSettings: TFormatSettings;
  date: Tdate;
  s: string;
begin
    s:= '01-01-05';
    FormatSettings.DateSeparator:= '-';
    FormatSettings.ShortDateFormat:= 'd/m/y';
    date := strtodate(s, FormatSettings);
    showmessage(datetostr(date));
end;


aber weil das zu langsam ist muss es direkt gehen, also hab ich mal das probiert:


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:
var
  FormatSettings: TFormatSettings;
  date: Tdate;
  s: string;
begin
    s:= '01-Aug-05';
    FormatSettings.DateSeparator:= '-';
    FormatSettings.ShortDateFormat:= 'd/m/y';
    Formatsettings.ShortMonthNames[1] := 'Jan';
    Formatsettings.ShortMonthNames[2] := 'Feb';
    Formatsettings.ShortMonthNames[3] := 'Mar';
    Formatsettings.ShortMonthNames[4] := 'Apr';
    Formatsettings.ShortMonthNames[5] := 'May';
    Formatsettings.ShortMonthNames[6] := 'Jun';
    Formatsettings.ShortMonthNames[7] := 'Jul';
    Formatsettings.ShortMonthNames[8] := 'Aug';
    Formatsettings.ShortMonthNames[9] := 'Sep';
    Formatsettings.ShortMonthNames[10] := 'Oct';
    Formatsettings.ShortMonthNames[11] := 'Nov';
    Formatsettings.ShortMonthNames[12] := 'Dec'
    date := strtodate(s, FormatSettings);
    showmessage(datetostr(date));
end;


es kommt aber der Fehler: '01-Aug-05' is not a valid date

PS:
weiss einer wie ich '01-Aug-05' effizient in '01-08-05' umwandeln kann mit stringreplace oder so


Delphi-Quelltext
1:
2:
s:= '01-Aug-05';
    stringreplace(s,'Aug','08',[rfReplaceAll, rfIgnoreCase]);


klappt nämlich ned??????? oder was mach ich falsch????

soll ich des alles mit pos delete insert machen oder wie????? wär ja voll lam das zeug!!!

Moderiert von user profile iconAXMD: Code- durch Delphi-Tags ersetzt.


StefanH - Fr 12.08.05 20:49
Titel: Re: String in Datum umwandeln z.B. '01-Jan-2005' in Tdate
user profile iconmolendrotter hat folgendes geschrieben:

Quelltext
1:
2:
s:= '01-Aug-05';
s:= stringreplace(s, 'Aug', '08', [rfReplaceAll, rfIgnoreCase]);


klappt nämlich ned??????? oder was mach ich falsch????


Ich denke, 2 Fragezeichen würden reichen, aber egal :roll: StringReplace ist eine Funktion.


Rolf_Geisler - Fr 12.08.05 20:52

Hallo,
für das, was Du vorhast, gibt es die Funktion StrToDate. Dort kannst Du über die FormatSettings auch die Monatsnamen und das Trennzeichen festlegen.
Schau mal in der Online-Hilfe nach.
Rolf


Rolf_Geisler - Sa 13.08.05 09:58

Sorry, bin ein wenig zerstreut. StrToDate verwendest Du ja schon. Aber vielleicht musst Du am ShortDateFormat etwas herumspielen. Könnte mir vorstellen, dass anstelle des Slash der (schon einmal definierten) Datumstrenner stehen muss. Win ist bei solchen Kleinigkeiten manchmal etwas eigen ... Habe da schon Merkwürdiges erlebt
Rolf


molendrotter - Sa 13.08.05 17:43

@stefanh
danke hat gut funktioniert, das ich das nicht gleich gesehen hab(-: is ja klar, die function gibt den string ja aus


@Rolf geißler
Zitat:

Aber vielleicht musst Du am ShortDateFormat etwas herumspielen. Könnte mir vorstellen, dass anstelle des Slash der (schon einmal definierten) Datumstrenner stehen muss.

also so meinst du?:

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:
var
  FormatSettings: TFormatSettings;
  date: Tdate;
  s: string;
begin
    s:= '01-Aug-05';
    FormatSettings.DateSeparator:= '-';
    FormatSettings.ShortDateFormat:= 'd-m-y';
    Formatsettings.ShortMonthNames[1] := 'Jan';
    Formatsettings.ShortMonthNames[2] := 'Feb';
    Formatsettings.ShortMonthNames[3] := 'Mar';
    Formatsettings.ShortMonthNames[4] := 'Apr';
    Formatsettings.ShortMonthNames[5] := 'May';
    Formatsettings.ShortMonthNames[6] := 'Jun';
    Formatsettings.ShortMonthNames[7] := 'Jul';
    Formatsettings.ShortMonthNames[8] := 'Aug';
    Formatsettings.ShortMonthNames[9] := 'Sep';
    Formatsettings.ShortMonthNames[10] := 'Okt';
    Formatsettings.ShortMonthNames[11] := 'Nov';
    Formatsettings.ShortMonthNames[12] := 'Dez';
    date := strtodate(s, FormatSettings);
    showmessage(datetostr(date));
end;


nee macht kein unterschied, hab ich schon probiert, haut ned hin, wo liegt nur mein fehler?
longdateformat und longmonthnames klappt au ned.
das gibts doch ned, is is ja zum verzweifeln.

Moderiert von user profile iconraziel: Code- durch Delphi-Tags ersetzt.


AXMD - Sa 13.08.05 17:51

Sollte ShortDateFormat nicht 'dd-mmm-yy' sein?

AXMD


StefanH - Sa 13.08.05 17:54

user profile iconAXMD hat folgendes geschrieben:
Sollte ShortDateFormat nicht 'dd-mmm-yy' sein?


hab ich mir grad auch gedacht. funktioniert bloß nicht. komisch :roll:


alzaimar - Sa 13.08.05 18:14

Was hält Dich davon ab, es so zu machen? Ist doch kompakt...

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Function ConvertDate (Const aDate : String) : String;
Const
  szMonths = 'JanFebMarAprMayJunJulAugSepOctNovDec';

Begin
  Result := Format('%s.%s.%s',[Copy (aDate,1,2),
    FormatFloat ('00',(Pos (Copy (aDate,4,3),szMonths) + 2div 3),
    Copy (aDate,8,2)])
End;

Und wenn Dir das Pos zu lahm sein sollte (wobei ich mich frage, in was für einer Applikation sowas eine Rolle spielt), dann baue Dir einen kleinen DEA, der die Monatsnamen in Monatsnummern überführt und scanne das Datum von links nach rechts. Schneller bekommt man das dann nämlich garantiert nicht hin. Das geht natürlich in Assembler noch viel schneller.

Woher kommen die Daten denn?


Delete - Sa 13.08.05 19:21

Also so geht es:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.Button1Click(Sender: TObject);
var
  MyDate                 : TDate;
  s                      : string;
begin
  s := '3-8-05';
  ShortDateFormat := 'dd-mmm-y';
  MyDate := StrToDate(s);
  ShowMessage(DateToStr(MyDate));
end;

Das Problem ist, im Datumsstring dürfen nur Zahlen vorkommen.

@alzaimar:
Also das mit deiner Stringkonstanten und dem copy ist ja wohl das schlechteste, was einem einfallen kann. Unperformanter geht es kaum noch. warum nimmst du nicht ein zwölf elementiges Array?


alzaimar - Sa 13.08.05 21:04

Hi Luckie.
Also, ich krieg das noch viel viel unperformanter hin, kein Problem. :D
Ein Array mit 12 Einträgen ist aber auch nicht viel schneller. Wenn schon, dann (wie ich ja schon erwähnte), ein DEA oder ein Dictionary, aber das ist angesichts der 36 Zeichen, die zu durchsuchen sind, sowieso eine kindische Überlegung, was denn hier nun der schnellste Algorithmus ist, ehrlich. Nächstes Mal solltest Du erst nachdenken (und lesen), und erst dann kritisieren. Aber Du schiesst gerne schnell, das ist schon ok.

Und wenn Du meine Bemerkung gelesen hast, dann ging es ums Kompakte (ich fands lustig, das Ganze in eine Zeile zu quetschen) und die Frage, wieso man sich überhaupt Gedanken über die Performance macht. Desweiteren hatte ich schon den DEA erwähnt, der dann das optimale Mittel ist, um in einem Durchgang sowohl Tag, als auch Monat und Jahr zu konvertieren. Und DAS ist dann schnell. Schlag Dir das mit dem Array lieber aus dem Kopf.


Sprint - Sa 13.08.05 21:40

user profile iconLuckie hat folgendes geschrieben:
Also das mit deiner Stringkonstanten und dem copy ist ja wohl das schlechteste, was einem einfallen kann. Unperformanter geht es kaum noch. warum nimmst du nicht ein zwölf elementiges Array?

*lol*

Vielleicht solltest du dich mal mit Delphi beschäftigen.


molendrotter - So 14.08.05 04:00

@alzheimer
Zitat:

Und wenn Dir das Pos zu lahm sein sollte (wobei ich mich frage, in was für einer Applikation sowas eine Rolle spielt)


wenn man 6,3GB Daten verarbeiten will spielt das eine sehhhhhhhher große rolle, zumal werden die Daten alle 1,5 Stunden neu angelegt, mehr als 2,5 Stunden darf es nicht dauern. Soviel zur performance, also die function wird milliardenmal aufgerufen(habs aber nicht gezählt) und bald wird es mehr und mehr...... und der PC wird nicht schneller!!!

zuerst hab ich suchen und ersetzten so geregelt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure strrepl(subo, subn: Stringvar s: String);
var
  i:integer;
begin
    repeat
    i:= posex(subo,s,i);
    if i<> 0 then begin
      delete(s,i,length(subo));
      insert(subn,s,i);
    end;
    until posex(subo,s,i) = 0;
end;

total lahm...............
dann hab ich die Monate wie 'Aug' mit:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
strrepl('Jan','01' ,s);
strrepl('Feb','02' ,s);
strrepl('Mar','03' ,s);
strrepl('Apr','04' ,s);
strrepl('May','05' ,s);
strrepl('Jun','06' ,s);
strrepl('Jul','07' ,s);
strrepl('Aug','08' ,s);
strrepl('Sep','09' ,s);
strrepl('Oct','10' ,s);
strrepl('Nov','11' ,s);
strrepl('Dec','12' ,s);

ersetzt bis ich verstanden hab wie das mit den Stringreplace Funktioniert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
s:= stringreplace(s, 'Jan','01',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Feb','02',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Mar','03',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Apr','04',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'May','05',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Jun','06',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Jul','07',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Aug','08',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Sep','09',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Oct','10',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Nov','11',[rfReplaceAll , rfIgnoreCase]);
s:= stringreplace(s, 'Dec','12',[rfReplaceAll , rfIgnoreCase]);


dann konnte ich wie gewohnt den String in ein Datum umwandeln, aber noch zu lahm und mit assembler kenn ich mich zu wenig aus als das ich sowas kompliziertes erstellen könnte.

und @luckie

das wusste ich auch schon von anfang an, dass es geht wenn man es nur mit zahlen macht, ich hab nur en problem damit den String in Zahlen umzuwanden und damit notwendige Performance zu verlieren, wenn es auch schneller und direk, quasi optimiert funktioniert.





wär toll wenn einer zeigt wie ich das vielleicht mit assembler lösen könnte(is ne 32bit Anwendung)




PS: performance ist zeitgewinn, zeit ist geld, mehr zeit ist mehr geld, und geld ist vergnügen, vergnügen ist programieren, programmieren ist performance....

Moderiert von user profile iconraziel: Code- durch Delphi-Tags ersetzt.


retnyg - So 14.08.05 04:50

luckie's idee mit dem array ist nicht so schlecht, immerhin 30 % schneller als das dauernde pos und copy.
auf alzaimar's code abgemünzt sähe das so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Function ConvertDate (Const aDate : String) : String;
Const
  szMonths : array [1..12of string[3] = ('Jan''Feb''Mar''Apr''May''Jun''Jul''Aug' , 'Sep''Oct''Nov''Dec');

Begin
  Result := Format('%s.%s.%s',[Copy (aDate,1,2),
    szMonths[strtoint(Copy (aDate,4,2))],
    Copy (aDate,7,2)])
End;

die funktion benötigt für 10,000,000 durchläufe auf meinem 3 ghz rechner 14 sekunden.
das sollte also durchaus ausreichen

aber so dauerts nur 1 sekunde:

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:
Function QuickConvertDate (Const aDate : String) : String;
// (C) retnyg
Const
  szMonths : array [1..12of string[3] = ('Jan''Feb''Mar''Apr''May''Jun''Jul''Aug' , 'Sep''Oct''Nov''Dec');
var m,i: integer;
    p:pchar;
Begin
  i:=1;
  if adAte[4] = '0' then m := ord(adate[5])-48
  else m := ord(adate[5])-38;
  setlength(result,9);
  p:=@result[1];
  repeat
    case i of
    1: p^:= aDate[1];
    2: p^:= aDate[2];
    3: p^:= '.';
    4: p^:= szMonths[m][1];
    5: p^:= szMonths[m][2];
    6: p^:= szMonths[m][3];
    7: p^:= '.';
    8: p^:= aDate[7];
    9: p^:= aDate[8];
    end;
    inc(p);
    inc(i);
  until i = 10;
End;



viel mehr lässt sich auch mit asm nicht rausholen

Moderiert von user profile iconraziel: Beiträge zusammengefasst.


alzaimar - So 14.08.05 10:35

Hi retnyg,
Die Aufgabe war: 03-Aug-05 --> 03.08.05. Was Du hier programmiert hast, ist aber : 03.08.05 --> 03-Aug-05. Das das schneller ist, und man für die Überführung von '08'->'Aug' ein Array nimmt, liegt auf der Hand.

Nachtrag:
1. Ich habe 'Aug'->8 mal als Version mit einer TStringlist (Sorted = True, IndexOf) probiert, aber das Ergebnis dauert doppelt so lang wie die POS-Variante. Wie programmiert man die Funktion 'Monatsnummer aus Name' mit einem Array möglichst schnell?

2. Hier also meine Version, die einen DEA benutzt, um den Monatsnamen in die Monatsnummer zu überführen. Man beachte die Komplexität im Vergleich zur 'grottenschlechten' aber eben minimalisitisch kompakten Version (Performancevergleich ca 10:1) sowie das völlige Fehlen einer Liste von Monatsnamen. Wir gehen stillschweigend davon aus, das nur gültige Monatsnamen im Datum vorkommen, eine Einschränkung, die auch für die Vergleichsalgorithmen gilt.


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:
// DateConvert
//
Var
  DateClass : Array [Char] Of ShortInt;

// Das hier soll eine Const-Deklaration sein, aber um zu verdeutlichen, wie
// die Daten zustande kommen, wurde eine Routine implementiert, die das Array
// füllt. Der Grundgedanke ist der, das 4 von 12 Monatsnamen im Englischen
// anhand des 1.Buchstabes klassifiziert werden können, der Rest kann durch
// Vergleiche des 2. bzw. 3. Buchstabens zugeordnet werden.
// DateClass nimmt nun die Klassifikation anhand des 1.Buchstabens vor.
// Ein positiver Wert bezeichnet den eindeutigen Monat, ein negativer Wert
// eine Fallunterscheidung. Man beachte, das die Gross/kleinschreibung des
// 1.Buchstabens ignoriert wird.

Procedure SetupDateClassConst;
Var
  i : Char;
Begin
  For i:= #0 to #255 do DateClass[i]:=0;
  DateClass ['j'] := -1; DateClass ['J'] := -1;
  DateClass ['f'] :=  2; DateClass ['F'] :=  2;
  DateClass ['m'] := -2; DateClass ['M'] := -2;
  DateClass ['a'] := -3; DateClass ['A'] := -3;
  DateClass ['s'] := 10; DateClass ['S'] := 10;
  DateClass ['n'] := 11; DateClass ['N'] := 11;
  DateClass ['D'] := 12; DateClass ['D'] := 12;
End;

Function ConvertDateFastest (Const aDate : String) : String;
Var
  m : Integer;
  
Begin
// ---- Monats-Hash à la Alzaimar mit DEA 2/3 Buchstabe ist case sensitiv ----
  m := DateClass[aDate[4]];
// Wenn < 0, dann Fallunterscheidung vornehmen
  case m Of
    -1 : -- Fälle Jan, Jun und Jul
      If aDate[5]='a' Then m := 1 else if aDate[6]= 'n' Then m := 6 else m:=7;
    -2 : -- Fälle Mar und May
      If aDate[6]='y' Then m:=5 Else m:=3;
    -3 : -- Fälle Apr und Aug
      If aDate[6]='r' Then m:=4 Else m:=8;
    End;
// ---------------------------------------------------------------------------
  SetLength (Result, 8);
  Result[1] := aDate[1]; // 1. Stelle des Tages kopieren
  Result[2] := aDate[2]; // 2. Stelle 
  Result[3] := '.';
  if m<10 then begin     // Monate 1..9 --> 01..09
    Result[4] := '0';
    Result[5] := Chr (m + 48);
    End
  Else Begin             // Monate 10,11 und 12 1:1
    Result[4] := '1';
    Result[5] := Chr (m + 38);
    End;
  Result[6] := '.';
  Result[7] := aDate[8]; // 1.Stelle Jahr
  Result[8] := aDate[9]; // 2.Stelle Jahr
End;

Das lässt sich normalerweise mit Assembler um einige Prozente steigern, oder?


retnyg - So 14.08.05 15:39

user profile iconalzaimar hat folgendes geschrieben:
Hi retnyg,
Die Aufgabe war: 03-Aug-05 --> 03.08.05. Was Du hier programmiert hast, ist aber : 03.08.05 --> 03-Aug-05. Das das schneller ist, und man für die Überführung von '08'->'Aug' ein Array nimmt, liegt auf der Hand.

ich habe mich einfach an den codeschnipsel von dir gehlaten und den optimiert :mrgreen:

user profile iconalzaimar hat folgendes geschrieben:
Das lässt sich normalerweise mit Assembler um einige Prozente steigern, oder?

ich habe mal meine quickconvert prozedur oben in assembler umgeschrieben. resultat: 18 % langsamer, als der von delphi optimierte asm-code.

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:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
Function ConvertDate3 (Const aDate : String) : String;
Const
  szMonths : array [1..12of string[3] = ('Jan''Feb''Mar''Apr''May''Jun''Jul''Aug' , 'Sep''Oct''Nov''Dec');
// das resultat der funktion ist nicht ganz richtig, es ging mir hier eher um den speed-test
begin
  asm
    pushad
  end;
  setlength(result,9);
  asm
    popad
    push ebx
    sub eax, 4
    test eax, eax
    jz @fin
    add eax, 8
    xor ecx, ecx
    cmp byte ptr[eax], 48
    jne @bigger10
    inc eax
    mov byte ptr[eax], cl
    sub cl , 48
//    push ecx
    jmp @haveMonth
    @bigger10:
    inc eax
    mov cl, byte ptr[eax]
    sub cl , 38
    //push ecx
    @haveMonth:
    sub eax, 5 // eax: adate, stack: m, ecx: i
    xor ecx, ecx
    mov edi, [result]
    mov edi, dword ptr [edi]

    @loopstart:
    mov edx, ecx
    lea  edx, edx*8
    lea edx, edx + @caseseg
    jmp +edx

    @caseseg:
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop
    nop

    @next:

    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop
    nop

    @next2:
    mov byte ptr[edi], 46
    jmp @nextround
    nop
    nop
    nop

    @next3: // unfinished
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop
    nop

    @next4:
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop
    nop

    @next5:
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop
    nop

    @next6:
    mov byte ptr[edi], 46
    jmp @nextround
    nop
    nop
    nop

    @next7:
    dec eax
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    jmp @nextround
    nop

    @next8:
    mov bl, byte ptr[eax]
    mov byte ptr[edi],   bl
    nop
    nop
    nop
    nop

    @nextround:
    inc eax
    inc edi
    inc ecx
    cmp ecx, 9
    jne @loopstart
    @ende:
  //  pop ecx
    xchg eax, edi
    @fin:
    pop ebx

  End;
end;

der delphi-compiler erzeugt ziemlich schnellen asm code, im normalfall besser als der coder.
für optimierungen bleibt genug platz im pascal-code ;)
edit: siehe hierzu ftp://ftp.untergrund.net/breakpoint/2005/after_the_party/seminars/bp05_seminars_-_Dierk_chaos_Ohlerich_-_Compilers_and_Assembler_-_xvid.avi


alzaimar - So 14.08.05 15:46

user profile iconretnyg hat folgendes geschrieben:
user profile iconalzaimar hat folgendes geschrieben:
Hi retnyg,
Die Aufgabe war: 03-Aug-05 --> 03.08.05. Was Du hier programmiert hast, ist aber : 03.08.05 --> 03-Aug-05. Das das schneller ist, und man für die Überführung von '08'->'Aug' ein Array nimmt, liegt auf der Hand.

ich habe mich einfach an den codeschnipsel von dir gehlaten und den optimiert :mrgreen:

Nee, hast Du nich, Du hast die Umkehrfunktion erzeugt (auch nicht schlecht), aber das ist nebensächlich. Was mich absolut umhaut ist die Tatsache, das dein ASM lahmer ist, als Delphi. Ich krieg bei ASM immer Pickel also hier meine Frage... obwohl ich die lieber in einem anderen Thread posten würde:
Wird eine Case Anweisung als Jumptable oder als Folge von Abfragen realisiert?


retnyg - So 14.08.05 15:57

user profile iconalzaimar hat folgendes geschrieben:
Was mich absolut umhaut ist die Tatsache, das dein ASM lahmer ist, als Delphi. Ich krieg bei ASM immer Pickel also hier meine Frage... obwohl ich die lieber in einem anderen Thread posten würde:
Wird eine Case Anweisung als Jumptable oder als Folge von Abfragen realisiert?

kannst du selber gucken: breakpoint auf das case-statement und dann STRG-ALT-C
der code den delphi generiert ist so kryptisch, dass es äusserst schwer ist ihn zu analysieren
er ist aber sehr effizient


alzaimar - So 14.08.05 16:10

Da muss ich widersprechen:

Delphi-Quelltext
1:
2:
3:
4:
5:
Case m of
  -1 : ...
  -2 : ...
  -3 : ...
end;

Erzeugt diesen Code (sinngemäss):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
m := m -3;
if m=0 goto x;
dec (m);

if m=0 goto y;
dec (m);

if m=0 goto z;
dec (m);
// else part
...

Andere Case-Konstrukte erzeugen ähnlichen Code. Mein alter UCSD-Compiler (68k System) erzeugt aber eine Jumptable...

Fazit: Um der (für das Fortbestehen der Welt, des Universums und dem ganzen Rest unverzichtbaren :dunce: ) ConvertDate Funktion zu noch mehr Performance zu verhelfen, kann man CASE-Anweisungen mit Hilfe von (vorher definierten) Jumptabellen noch optimieren. Ich postuliere mal, das eine Anweisung:

Delphi-Quelltext
1:
Goto JumpTable [m];                    

das iterative Delphi-Compilat doch noch um Längen schlägt...


retnyg - So 14.08.05 16:22

user profile iconalzaimar hat folgendes geschrieben:
Ich postuliere mal, das eine Anweisung:

Delphi-Quelltext
1:
Goto JumpTable [m];                    

das iterative Delphi-Compilat doch noch um Längen schlägt...

das wage ich zu bezweifeln, denn im endeffekt kennt der assembler eh nur jumps

ich wollte grade mal deine funktion testen, aber der compiler meckert rum...
kannst du mal eine lauffähige variante posten ?


alzaimar - So 14.08.05 16:24

Läuft hier:

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:
// Das hier soll eine Const-Deklaration sein, aber um zu verdeutlichen, wie
// die Daten zustande kommen, wurde eine Routine implementiert, die das Array
// füllt. Der Grundgedanke ist der, das 4 von 12 Monatsnamen im Englischen
// anhand des 1.Buchstabes klassifiziert werden können, der Rest kann durch
// Vergleiche des 2. bzw. 3. Buchstabens zugeordnet werden.
// DateClass nimmt nun die Klassifikation anhand des 1.Buchstabens vor.
// Ein positiver Wert bezeichnet den eindeutigen Monat, ein negativer Wert
// eine Fallunterscheidung
Var
  DateClass : Array [Char] Of ShortInt;

Procedure SetupDateClassConst;
Var
  i : Char;
Begin
  For i:= #0 to #255 do DateClass[i]:=0;
  DateClass ['j'] := -1; DateClass ['J'] := -1;
  DateClass ['f'] :=  2; DateClass ['F'] :=  2;
  DateClass ['m'] := -2; DateClass ['M'] := -2;
  DateClass ['a'] := -3; DateClass ['A'] := -3;
  DateClass ['s'] := 10; DateClass ['S'] := 10;
  DateClass ['n'] := 11; DateClass ['N'] := 11;
  DateClass ['D'] := 12; DateClass ['D'] := 12;
End;

Function ConvertDateFastest (Const aDate : String) : String;
Var
  m : Integer;

Begin
// ---- Monats-Hash à la Alzaimar --------------------------------------------
  m := DateClass[aDate[4]];
// Wenn < 0, dann Fallunterscheidung vornehmen
  case m Of
    -1 : If aDate[5]='a' Then m := 1 else if aDate[6]= 'n' Then m := 6 else m:=7;
    -2 : If aDate[6]='y' Then m:=5 Else m:=3;
    -3 : If aDate[6]='r' Then m:=4 Else m:=8;
    End;
//----------------------------------------------------------------------------
  SetLength (Result, 8);
  Result[1] := aDate[1];
  Result[2] := aDate[2];
  Result[3] := '.';
  if m<10 then begin
    Result[4] := '0';
    Result[5] := Chr (m + 48);
    End
  Else Begin
    Result[4] := '1';
    Result[5] := Chr (m + 38);
    End;
  Result[6] := '.';
  Result[7] := aDate[8];
  Result[8] := aDate[9];
End;


Einmal 'SetupDateClassConst' aufrufen und dann loslegen. Aber, nochmal. Die Funktion liefert:

ConvertDateFastest ('01-Aug-05')-->'01.08.05'


retnyg - So 14.08.05 16:56

bei deiner prozedur kriege ich immer 00 als monat, egal welchen monatsnamen ich teste.

//edit aha, habe vergessen die setup-funktion aufzurufen :shock:

hier meine rückumwandel-prozedur, ist im günstigsten fall 8%, und im schlechtesten fall 2 % schneller als deine user defined image
ausserdem ist sie 10 mal schneller, als die konvertierung in die andere richtung.

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:
Function QuickConvertDateBack (Const aDate : String) : String;
// (C) retnyg
Const
  szMonths : array [1..12of string[3] = ('Jan''Feb''Mar''Apr''May''Jun''Jul''Aug' , 'Sep''Oct''Nov''Dec');
var m,i: integer;
    p,p2:pchar;
    m1,m2:char;
Begin
  p := @szMonths[1][1];
  for m := 1 to 12 do begin
     if p^ = adate[4then begin
        inc(p);
        if p^ = adate[5then begin
           inc(p);
           if p^ = adate[6then break;
           dec(p);
        end;
        dec(p);
     end;
     inc(p,4);
  end;
  if m > 9 then begin
     m1 := '1';
     dec(m,10);
     m2 := chr(m+48);
  end
  else begin
     m1 := '0';
     m2 := chr(m+48);
  end;

  setlength(result,8);
  p:=@result[1];
  i:=1;
  repeat
    case i of
    1: p^:= aDate[1];
    2: p^:= aDate[2];
    3: p^:= '.';
    4: p^:= m1;
    5: p^:= m2;
    6: p^:= '.';
    7: p^:= aDate[8];
    8: p^:= aDate[9];
    end;
    inc(p);
    inc(i);
  until i = 9;
End


alzaimar - So 14.08.05 17:19

Du musst schon DateClass initialisieren ...

Delphi-Quelltext
1:
SetupDateClassConst;                    

Übrigens ist das hier wieder schneller :beer: (Du bist also wieder am Zug)

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:
Function ConvertDateFastest (Const aDate : String) : String;
Var
  m,i : Integer;
  p : PChar;

Begin

// ---- Monats-Hash à la Alzaimar --------------------------------------------
  m := DateClass[aDate[4]];
// Wenn < 0, dann Fallunterscheidung vornehmen
  case m Of
    -1 : If aDate[5]='a' Then m := 1 else if aDate[6]= 'n' Then m := 6 else m:=7;
    -2 : If aDate[6]='y' Then m:=5 Else m:=3;
    -3 : If aDate[6]='r' Then m:=4 Else m:=8;
    End;
//----------------------------------------------------------------------------
  SetLength (Result, 8);
  p := @Result[1];
  p^ := aDate[1]; inc (p);
  p^ := aDate[2]; inc (p);
  p^ := '.'; inc (p);
  if m<10 then begin
    p^ := '0'; inc (p);
    p^ := Chr (m + 48); inc(P);
    End
  Else Begin
    p^ := '1'; inc (p);
    p^ := Chr (m + 38); inc (p);
    End;
  p^ := '.'; inc (p);
  p^ := aDate [8]; inc (p);
  p^ := aDate [9];
End;


retnyg - So 14.08.05 18:14

ich glaube nicht dass man noch mehr rausholen kann
deine vergleichstechnik ist ziemlich effektiv, in verbindung mit dem pointerzugriff optimal. user defined image
allein die setuproutine bietet noch optmierungsbedarf...


retnyg - So 14.08.05 18:49

doch noch was rausgekommen ^^

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:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
Function QuickConvertDateBack (const aDate : String) : String;
// (C) retnyg
var p,p3:pchar;
    mn:dword;
Begin
  setlength(result,8);
  p:=@result[1];
  p^:= aDate[1];
  inc(p);
  p^:= aDate[2];
  inc(p);
  p^:= '.';
  inc(p);


  cardinal(p3) := cardinal(@adate[4]);
  mn:=pdword(p3)^;
  mn := mn shl 8;
  mn := mn shr 8;
  case mn of
    $006e614a:
      begin
         p^:='0';
         inc(p);
         p^:='1';
      end;
    $00626546:
      begin
         p^:='0';
         inc(p);
         p^:='2';
      end;
    $0072614d:
      begin
         p^:='0';
         inc(p);
         p^:='3';
      end;

    $00727041:
      begin
         p^:='0';
         inc(p);
         p^:='4';
      end;

    $0079614d:
      begin
         p^:='5';
         inc(p);
         p^:='1';
      end;
    $006e754a:
      begin
         p^:='0';
         inc(p);
         p^:='6';
      end;
    $006c754a:
      begin
         p^:='0';
         inc(p);
         p^:='7';
      end;
    $00677541:
      begin
         p^:='0';
         inc(p);
         p^:='8';
      end;
    $00706553:
      begin
         p^:='0';
         inc(p);
         p^:='9';
      end;
    $0074634f:
      begin
         p^:='1';
         inc(p);
         p^:='0';
      end;
    $00766f4e:
      begin
         p^:='1';
         inc(p);
         p^:='1';
      end;
    $00636544:
      begin
         p^:='1';
         inc(p);
         p^:='2';
      end;
  end;
  inc(p);
  p^:= '.';
  inc(p);
  p^:= aDate[8];
  inc(p);
  p^:= aDate[9];
End;


alzaimar - So 14.08.05 20:05

user profile iconretnyg hat folgendes geschrieben:
user defined image
allein die setuproutine bietet noch optmierungsbedarf...

Ich hab doch im Code vermerkt, das man das als Const-Delkaration schreiben kann:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Const 
  DateClass : Array [Char] Of SmallInt = (
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0, -3,  0,  012,  0,  2,  0,  0,  0, -1,  0,  0, -211,  0,
  0,  0,  010,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0, -3,  0,  0,  0,  0,  2,  0,  0,  0, -1,  0,  0, -211,  0,
  0,  0,  010,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
  );

Aber dann isses ja nicht mehr so übersichtlich.


molendrotter - So 14.08.05 21:48

Thanks @all

is ja wirklich toll eure rege beteiligung

ich hab mich jetzt für die wirklich schnellere methode von alzaimar entschlossen is wirklich schon ziemlich schnell also 50 mal schneller als die Methode die ich vorherangewendet habe SUPERKLASSE

stellt sich nur noch die Frage, ob die direkte Umwandlung mit Stringtodate, wenn sie denn so funktioniert überhaupt noch schneller ist (-:

1000000000 Einträge in 8,733 min (AMD Athlon 1,2GH) echt klasse, wie der erst suf meinem Dualcore(2x3,0 pentium 4) abgeht.

hoffentlich haben wir nicht übersehen, das es auch ohne umwandlung des Strings in 'Zahlendatum' geht, also direkt, wär ja ne Verschwendung von so viel programmiererischen Könnens aller hir im Forum,

noch mals vielen Dank!!!!

ich lass die Frage nochmal en weilchen offen stehn fals einer alzaimar übertrumpfen will, oder meint seine methode sei schneller.

PS: leider kann ich den rückumwandlungscode nicht gebrauchen, aber der is auch super, danke @retnyg :D


retnyg - So 14.08.05 22:29


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:
85:
86:
87:
88:
89:
90:
Function QuickConvertDateBackAsm (const aDate : String) : String;
Begin
  asm push eax end;
  setlength(result,8);
  asm
  pop eax
  mov ecx, result
  mov ecx, [ecx]
  mov edi, dword ptr[eax]
  mov [ecx], edi
  mov edi, dword ptr[eax+5]
  mov [ecx+4], edi
  mov edx, dword ptr[eax+3]
  shl edx, 8
  shr edx, 8

  cmp edx, $006e614a
  jne @b2
  mov word ptr[ecx+3], $3130
  jmp @fin

  @b2:
  cmp edx, $00626546
  jne @b3
  mov word ptr[ecx+3], $3230
  jmp @fin

  @b3:
  cmp edx, $0072614d
  jne @b4
  mov word ptr[ecx+3], $3330
  jmp @fin

  @b4:
  cmp edx, $00727041
  jne @b5
  mov word ptr[ecx+3], $3430
  jmp @fin

  @b5:
  cmp edx, $0079614d
  jne @b6
  mov word ptr[ecx+3], $3530
  jmp @fin

  @b6:
  cmp edx, $006e754a
  jne @b77
  mov word ptr[ecx+3], $3630
  jmp @fin

  @b77:
  cmp edx, $006c754a
  jne @b7
  mov word ptr[ecx+3], $3730
  jmp @fin

  @b7:
  cmp edx, $00677541
  jne @b8
  mov word ptr[ecx+3], $3830
  jmp @fin

  @b8:
  cmp edx, $00706553
  jne @b9
  mov word ptr[ecx+3], $3930
  jmp @fin

  @b9:
  cmp edx, $0074634f
  jne @b10
  mov word ptr[ecx+3], $3031
  jmp @fin

  @b10:

  cmp edx, $00766f4e
  jne @b11
  mov word ptr[ecx+3], $3131
  jmp @fin

  @b11:

  cmp edx, $00636544
  db $75$0A
  mov word ptr[ecx+3], $3231
  @fin:
  end;
End;

8,3 sekunden bei 100 mill. aufrufen VS 8,7 bei dir [url=http://www.planet-smilies.de/]user defined image[/url] :mrgreen:


molendrotter - So 14.08.05 22:44

cool retnyg :D

ned schlecht ich glaub ich lern auch en bissel mehr assembler das ich sowas auch hinkrieg,

[highlight]
aber mir is was aufgefallen, in der realanwendung der Methode von alzaimar wird oft das Datum falsch umgewandelt, und macht ihn sie somit nutzlos :( vielleicht hab ich ja was flasch kopiert aber manchmal kommen Strings raus wie: '2-.00.5' und so , das is ned so toll, schade jetzt hab ich mich so darauf gefreut das er funzt, mist verdammter :evil: [/highlight]

@alzaimar und die anderen vielleicht merkt einer woran das liegt?


retnyg - So 14.08.05 22:52

verwendest du deutsche oder englische monatsnamen ?
jan feb mar... --> dann kannste auch meine neue asm prozeudr hernehmen
oder jan feb mär --> bescheid geben dann pass ich die funktion noch an


Sprint - So 14.08.05 23:07

Du musst nicht gleich Assembler lernen.

Meine Lösung:

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:
function ConvertDate(const ADate: String): String;
var
  PResult: PByte;
  PADate: PByte;
  M: Integer;
begin
  SetLength(Result, 8);
  PResult := Pointer(Result);
  PADate := Pointer(ADate);
  PByteArray(PResult)[0] := PByteArray(PADate)[0];
  PByteArray(PResult)[1] := PByteArray(PADate)[1];
  PByteArray(PResult)[2] := Ord('.');
  case ADate[4of
    'A'if ADate[5] = 'p' then
           M := 4
         else
           M := 8;
    'D': M := 12;
    'F': M := 2;
    'J'if ADate[5] = 'a' then
           M := 1
         else if ADate[6] = 'l' then
           M := 7
         else
           M := 6;
    'M'if ADate[6] = 'r' then
           M := 3
         else
           M := 5;
    'N': M := 11;
    'O': M := 10;
    'S': M := 9;
    else
      M := 0;
  end;
  if M < 10 then
    PByteArray(PResult)[3] := Ord('0')
  else
    PByteArray(PResult)[3] := Ord('1');
  PByteArray(PResult)[4] := Byte(Chr(48 + M));
  PByteArray(PResult)[5] := Ord('.');
  PByteArray(PResult)[6] := PByteArray(PADate)[7];
  PByteArray(PResult)[7] := PByteArray(PADate)[8];
end;


Konvertiert ein Datum von 01-Aug-05 in 01.08.05
Gültige Monatsabkürzungen: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec

In meinem Test ist diese Funktion ein kleines bißchen schneller, als retnyg Assembler-Weg.

Ich würde aber auf diese Lösung verzichten, da Sicherheit und Stabilität bei mir vor Geschwindigkeit stehen.


Delete - Mo 15.08.05 00:02

user profile iconSprint hat folgendes geschrieben:
user profile iconLuckie hat folgendes geschrieben:
Also das mit deiner Stringkonstanten und dem copy ist ja wohl das schlechteste, was einem einfallen kann. Unperformanter geht es kaum noch. warum nimmst du nicht ein zwölf elementiges Array?

*lol*

Vielleicht solltest du dich mal mit Delphi beschäftigen.

Stringoperationen sind, so viel ich weiß, nie sehr performant.


retnyg - Mo 15.08.05 00:03

user profile iconSprint hat folgendes geschrieben:

In meinem Test ist diese Funktion ein kleines bißchen schneller, als retnyg Assembler-Weg.
In meinem test ist deine funktion mit 9,7 sek deutlich langsamer.
ausserdem produziert sie 04.1<.05 bei '04-dec-05'


Sprint - Mo 15.08.05 00:11

user profile iconretnyg hat folgendes geschrieben:
ausserdem produziert sie 04.1<.05 bei '04-dec-05'

Peinlich. Hatte vorher ein Array und vergessen das anzupassen.


Sprint - Mo 15.08.05 00:41

Nochmal überarbeitet:

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:
function ConvertDate(const ADate: String): String;
var
  PResult: PByte;
  PADate: PByte;
  M: Integer;
begin
  SetLength(Result, 8);
  PResult := Pointer(Result);
  PADate := Pointer(ADate);
  PByteArray(PResult)[0] := PByteArray(PADate)[0];
  PByteArray(PResult)[1] := PByteArray(PADate)[1];
  PByteArray(PResult)[2] := Ord('.');
  case ADate[4of
    'A'if ADate[5] = 'p' then
           M := 4
         else
           M := 8;
    'D': M := 12;
    'F': M := 2;
    'J'if ADate[5] = 'a' then
           M := 1
         else if ADate[6] = 'l' then
           M := 7
         else
           M := 6;
    'M'if ADate[6] = 'r' then
           M := 3
         else
           M := 5;
    'N': M := 11;
    'O': M := 10;
    'S': M := 9;
    else
      M := 0;
  end;
  if M < 10 then
  begin
    PByteArray(PResult)[3] := Ord('0');
    PByteArray(PResult)[4] := Byte(Chr(48 + M));
  end else
  begin
    PByteArray(PResult)[3] := Ord('1');
    PByteArray(PResult)[4] := Byte(Chr(38 + M));
  end;
  PByteArray(PResult)[5] := Ord('.');
  PByteArray(PResult)[6] := PByteArray(PADate)[7];
  PByteArray(PResult)[7] := PByteArray(PADate)[8];
end;


@retnyg: Ich bekomme bei deinem Code immer einen externen Fehler. Weiß aber nicht warum.


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:
procedure TForm1.Button1Click(Sender: TObject);
const
  Month: array[1..12of String = ('Jan''Feb''Mar''Apr''May''Jun',
    'Jul''Aug''Sep''Oct''Nov''Dec');
  Year: array[1..3of String = ('05''06''07');
var
  I, J, K, L: Integer;
  S: String;
  Frequency, Start, Stop: Int64;
begin

  QueryPerformanceFrequency(Frequency);
  QueryPerformanceCounter(Start);

  for I := 1 to 10000 do
    for J := Low(Year) to High(Year) do
      for K := Low(Month) to High(Month) do
        for L := 1 to 28 do
        begin
          S := FormatFloat('00', L) + '-' + Month[K] + '-' + Year[J];
          S := QuickConvertDateBackAsm(S);
        end;

  QueryPerformanceCounter(Stop);
  ShowMessage(FormatFloat('0.00', (Stop - Start) * 1000 / Frequency) + ' Millisekunden');

end;


Wenn du Lust hast, kannst du ja mal beide Beispiele testen.


retnyg - Mo 15.08.05 01:35

dein beispiel benötigt bei deinem test 15123,24 ms, meines 15047,31 ms
bei meinem test 54,4 sek (sprintConvert) vs 52,3 sek (asmConvert)
getestet auf nem intel p4 3.0 HT

wundert mich dass der asm-code bei dir nicht läuft - an welcher stelle meckert er ?



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TForm1.Button2Click(Sender: TObject);
var zeit: int64; i,j : integer;  s:string;
Const
  szMonths : array [1..12of string[3] = ('Jan''Feb''Mar''Apr''May''Jun''Jul''Aug' , 'Sep''Oct''Nov''Dec');
begin
  zeit := gettickcount ;
 // setupdateclassconst;
  for i := 1 to 10000000 do
    for j := 1 to 12 do
  //s:=convertdatefastest('04-'+szMonths[j]+'-05');
  s:=quickconvertdatebackasm('04-'+szMonths[j]+'-05');
//    s:=sprintconvertdate('04.Dec.05');
  //s:=spconvertdate('04-'+szMonths[j]+'-05');
  memo1.Lines.add(s);
  showmessage(inttostr((gettickcount - zeit) div 100));

end;


edit: sehe gerade dass meine asm-prozedur probleme bereitet, wenn man optimierung in den compileroptionen einschaltet ! werde das prüfen und dann ein update posten


Sprint - Mo 15.08.05 02:45

Also ich habe mal die Case Anweisung durch eine If Abfrage ersetzt. Ist dadurch einwenig schneller geworden.

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:
function ConvertDate(const ADate: String): String;
var
  PResult: PByte;
  PADate: PByte;
  M: Integer;
begin
  SetLength(Result, 8);
  PResult := Pointer(Result);
  PADate := Pointer(ADate);
  PByteArray(PResult)[0] := PByteArray(PADate)[0];
  PByteArray(PResult)[1] := PByteArray(PADate)[1];
  PByteArray(PResult)[2] := Ord('.');
  if ADate[4] = 'D' then
    M := 12
  else if ADate[4] = 'F' then
    M := 2
  else if ADate[4] = 'N' then
    M := 11
  else if ADate[4] = 'O' then
    M := 10
  else if ADate[4] = 'S' then
    M := 9
  else if ADate[4] = 'A' then
  begin
    if ADate[5] = 'p' then
      M := 4
    else
      M := 8;
  end
  else if ADate[4] = 'J' then
  begin
    if ADate[5] = 'a' then
      M := 1
    else if ADate[6] = 'l' then
      M := 7
    else
      M := 6;
  end
  else if ADate[4] = 'M' then
  begin
    if ADate[6] = 'r' then
      M := 3
    else
      M := 5;
  end
  else
    M := 0;
  if M < 10 then
  begin
    PByteArray(PResult)[3] := Ord('0');
    PByteArray(PResult)[4] := Byte(Chr(48 + M));
  end else
  begin
    PByteArray(PResult)[3] := Ord('1');
    PByteArray(PResult)[4] := Byte(Chr(38 + M));
  end;
  PByteArray(PResult)[5] := Ord('.');
  PByteArray(PResult)[6] := PByteArray(PADate)[7];
  PByteArray(PResult)[7] := PByteArray(PADate)[8];
end;


Da ich den Fehler nicht lokalizieren kann, habe ich jeden Monat 5.000.000 durchlaufen lassen.
Das sind meine Ergebnisse:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
                Sprint  retnyg
-----------------------------------
01-Jan-05       392,63  358,97
01-Feb-05       370,01  375,81
01-Mar-05       394,14  377,25
01-Apr-05       381,68  371,78
01-May-05       397,00  384,05
01-Jun-05       401,17  381,27
01-Jul-05       393,70  384,38
01-Aug-05       385,37  388,38
01-Sep-05       377,91  400,07
01-Oct-05       381,13  397,44
01-Nov-05       379,08  405,25
01-Dec-05       369,79  406,30

                385,30  385,91


Getestet auf einem AMD 2600+


Edit: \\
Fazit: Richtig angewendet, macht der Delphi Compiler aus Object Pascal sehr guten Code.


molendrotter - Mo 15.08.05 11:24

@retnyg: Gültige Monatsabkürzungen: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec sollen es sein.

dein code Sprint ist auch nicht von schlechten eltern, mich wundert nur das die ASM_lösung ungefähr die gleiche Performance bringt (ich hab mal gelert das ASM immer mindestens 20% mehr bringt gilt bei heutigen sachen wohl nicht mehris auch schon 7 Jahre her).

mal ne frage was für delphi versionen verwendet ihr vielleicht gibts da ja wieder Unterschiede im Compiler(bessere Compiler ab D8 ????)
werd mir heute Abend mal alle sorten selbst durchchecken und ermitteln welche die schnellste und zuverlässigste ist,

wieder mal vielen dank für die tollen codes, aber kann man da nicht noch was combiniern: en bissel ASM en bissel pascal von einem das von anderen dies, vielleicht bringts ja was, wed mich auch mal daran versuchen

:D


retnyg - Mo 15.08.05 14:19

so, hab nun endlich ne version fertig die auch mit optimization tadellos funktioniert.
ist die optimierung aktiviert, ist sprints methode tatsächlich um ein quäntchen schneller
// edit: loooool, eine anweisung mit 2 byte wegoptimiert jetzt ist meines wieder schneller user defined image

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:
85:
86:
87:
88:
89:
90:
91:
92:
93:
Function AsmConvertDate (const aDate : String) : String;
//edx: result, eax: aDate
asm
  push eax
  push edx
  xchg eax, edx
  xor edx, edx
  mov dl, 8

  call system.@LStrSetLength

  pop eax
  pop edx
  mov eax, [eax] // RESULT

  mov ECX, dword ptr[EDX]
  mov [EAX], ECX
  mov ECX, dword ptr[EDX+5]
  mov [EAX+4], ECX
  mov ECX, dword ptr[EDX+2]
  mov cl, 0

  cmp ECX, $6e614a00
  jne @b2
  mov word ptr[EAX+3], $3130
  jmp @fin

  @b2:
  cmp ECX, $62654600
  jne @b3
  mov word ptr[EAX+3], $3230
  jmp @fin

  @b3:
  cmp ECX, $72614d00
  jne @b4
  mov word ptr[EAX+3], $3330
  jmp @fin

  @b4:
  cmp ECX, $72704100
  jne @b5
  mov word ptr[EAX+3], $3430
  jmp @fin

  @b5:
  cmp ECX, $79614d00
  jne @b6
  mov word ptr[EAX+3], $3530
  jmp @fin

  @b6:
  cmp ECX, $6e754a00
  jne @b77
  mov word ptr[EAX+3], $3630
  jmp @fin

  @b77:
  cmp ECX, $6c754a00
  jne @b7
  mov word ptr[EAX+3], $3730
  jmp @fin

  @b7:
  cmp ECX, $67754100
  jne @b8
  mov word ptr[EAX+3], $3830
  jmp @fin

  @b8:
  cmp ECX, $70655300
  jne @b9
  mov word ptr[EAX+3], $3930
  jmp @fin

  @b9:
  cmp ECX, $74634f00
  jne @b10
  mov word ptr[EAX+3], $3031
  jmp @fin

  @b10:

  cmp ECX, $766f4e00
  jne @b11
  mov word ptr[EAX+3], $3131
  jmp @fin

  @b11:
  //cmp ECX, $63654400
  mov word ptr[EAX+3], $3231
  @fin:
End;


user profile iconmolendrotter hat folgendes geschrieben:
@retnyg: Gültige Monatsabkürzungen: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec sollen es sein.

mit denen kommt mein code eh zugang.

user profile iconmolendrotter hat folgendes geschrieben:
dein code Sprint ist auch nicht von schlechten eltern, mich wundert nur das die ASM_lösung ungefähr die gleiche Performance bringt (ich hab mal gelert das ASM immer mindestens 20% mehr bringt gilt bei heutigen sachen wohl nicht mehris auch schon 7 Jahre her).

der delphi compiler erzeugt schlussendlich auch assemblercode, dazu noch hochoptimierten.
wenn natürlich der programmierer 20 pos und copy in seinem code hat, macht der compiler halt hochoptimierten pos und copy - code, der aber trotzdem extrem langsam ist.

wenn man schnellen code haben will, ist also mal der erste schritt zu überlegen wo performance verloren geht, und ob es nicht einen anderen weg gibt.

bei strings gibt es da mehrere tricks, z.b. am anfang gleich die länge festzulegen und dann direkt die einzelnen bytes zu schreiben.


Delphi-Quelltext
1:
strtoint(copy(adate, 4,2))                    

hier muss z.b. extra ein neuer string angelegt werden, wegen copy. das dauert einige 100 cpu-zyklen
darum:

Delphi-Quelltext
1:
2:
  if adAte[4] = '0' then m := ord(adate[5])-48
  else m := ord(adate[5])-38;



der vorteil vom manuellen asm-coden ist nur dass man direkt auf die register zugreifen kann

Delphi-Quelltext
1:
2:
3:
4:
  mov ECX, dword ptr[EDX]
  mov [EAX], ECX
  mov ECX, dword ptr[EDX+5]
  mov [EAX+4], ECX

mit diesen 4 zeilen asm habe ich bereits in result den string '04-Jn-05'
der rest ist dann eigentlich nur noch, bei Jn die passende monatsnummer einzufügen


alzaimar - Mo 15.08.05 16:14

user profile iconmolendrotter hat folgendes geschrieben:

[highlight]
aber mir is was aufgefallen, in der realanwendung der Methode von alzaimar wird oft das Datum falsch umgewandelt, und macht ihn sie somit nutzlos :( vielleicht hab ich ja was flasch kopiert aber manchmal kommen Strings raus wie: '2-.00.5' und so , das is ned so toll, schade jetzt hab ich mich so darauf gefreut das er funzt, mist verdammter :evil: [/highlight]

@alzaimar und die anderen vielleicht merkt einer woran das liegt?

Ja, an der Setup-Prozedur. Der Monat Oktober ('O') wird gar nicht, der September falsch erkannt. Hier das entsprechende SetupDateClass sowie die geänderten Zeilen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
Procedure SetupDateClassConst;
Var
  i : Char;

Begin
  For i:= #0 to #255 do DateClass[i]:=0;
  DateClass ['j'] := -1; DateClass ['J'] := -1;
  DateClass ['f'] :=  2; DateClass ['F'] :=  2;
  DateClass ['m'] := -2; DateClass ['M'] := -2;
  DateClass ['a'] := -3; DateClass ['A'] := -3;
  DateClass ['s'] :=  9; DateClass ['S'] :=  9;
  DateClass ['o'] := 10; DateClass ['O'] := 10;

  DateClass ['n'] := 11; DateClass ['N'] := 11;
  DateClass ['d'] := 12; DateClass ['D'] := 12;
End;

Die Const-Deklaration (alternativ) ist dann:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Const
  DateClass : Array [Char] Of Smallint = (
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0, -3,  0,  012,  0,  2,  0,  0,  0, -1,  0,  0, -21110,
  0,  0,  0,  9,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0, -3,  0,  0,  0,  0,  2,  0,  0,  0, -1,  0,  0, -211,  0,
  0,  0,  0,  9,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
);

Trozdem ist mein Code am Schnellsten, auch schneller als das ASM-Zeugs. Das beweist, das es in erster Linie auf das richtige Verfahren ankommt und DANN erst auf die Programmiersprache. Ich verwende einen DEA, das ist einer Jumptabelle nicht unähnlich. So etwas ist immer schneller als iterative Ansätze wie eine die x86-Umsetzung von Delphi für eine CASE-Anweisung.

Mich würde nun noch interessieren, ob eine ASM-Implementierung von meinem Zeugs noch mal was bringt...


retnyg - Mo 15.08.05 17:02

user profile iconalzaimar hat folgendes geschrieben:

Trozdem ist mein Code am Schnellsten, auch schneller als das ASM-Zeugs.

tatsächlich ? ich habe gerade alle 3 algo's verglichen, jeweils die aktuellste version
hier die resultate

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
retnyg       sprint       alzaimar
============ ============ ============
8190,80      8327,52      X 8395,43
X 8196,26    8326,91      8390,86
X 8177,86    X 8341,98    8387,98
8190,00      X 8316,46    8393,55
8191,19      8328,56      8389,69
8181,96      8330,2       8387,3
8196,00      8316,99      8392,68
8181,52      8323,19      8392,02
8185,92      8331,25      8388,32
8191,22      8323,45      X 8384,16
============ ============ ============
8188,58 ms   8326,01 ms   8390,3 ms
100,00       101,68       102,46  in prozent (weniger ist besser)

mein prüf-setup macht 10 messungen hintereinander, und streicht dann jeweils den höchsten, sowie den niedrigsten wert, um schwankungen auszugleichen. die restlichen 8 werte werden dann zur durchschnittsberechnung herangezogen
du kannst das gerne nachprüfen:

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:
procedure TForm1.Button3Click(Sender: TObject);
const
  Month: array[1..12of String = ('Jan''Feb''Mar''Apr''May''Jun',
    'Jul''Aug''Sep''Oct''Nov''Dec');
  Year: array[1..3of String = ('05''06''07');
var
  I, J, K, L: Integer;
  S: String;
  Frequency, Start, Stop: Int64;
  result: array [1..12of double;
  smallest, biggest, sum: double;
begin
  setupdateclassconst;
  for l := 1 to 10 do begin

    QueryPerformanceFrequency(Frequency);
    QueryPerformanceCounter(Start);

    for I := 1 to 1000000 do
        for K := Low(Month) to High(Month) do
          begin
            S := '04-' + Month[K] + '-05';
            //S := asmConvertDate(S);
            //s:=convertdatefastest(s);
            s:=spconvertdate3(s);
          end;

    QueryPerformanceCounter(Stop);
    result[l]:=(Stop - Start) * 1000 / Frequency;
    //memo1.Lines.add(FormatFloat('0.00', result[l] + ' Millisekunden');
    application.ProcessMessages;
  end;
  smallest := maxint;
  biggest := 0; sum := 0;
  for l := 1 to 10 do begin
    if result[l] < smallest then smallest := result[l];
    if result[l] > biggest then biggest := result[l];
  end;
  for l := 1 to 10 do begin
     if (result [l] = smallest) or (result [l] = biggest) then
     memo1.Lines.add( 'X ' + FormatFloat('0.00', result[l]) )
     else begin
       memo1.Lines.add(FormatFloat('0.00', result[l] ));
       sum := sum + result[l];
     end;
  end;
  memo1.Lines.add('============');
  memo1.Lines.add(FormatFloat('0.00', sum / 8 ));
end;

user profile iconalzaimar hat folgendes geschrieben:
Mich würde nun noch interessieren, ob eine ASM-Implementierung von meinem Zeugs noch mal was bringt...

wenn du es optimal umsetzt, ist es vielleicht schneller als meines.
meine routine baut hauptsächlich darauf, dass die vergleichswerte 32 bit gross sind, was maximale performance erlaubt.


alzaimar - Mo 15.08.05 18:29

Poste mal die komplette Test-App, mir scheint es liegt an den Prozessoren.
Auf meinem Pentium M (1.5GHz) ist meine Routine ca. 10% schneller als die ASM-Teile von Dir....
Ich habe einfach '05-xxx-05' für alle xxx='Jan'...'Dez' jeweils 5.000.000 x aufgerufen, und dein ASM liegt bei 12.95 mein Zeugs bei 11.49, also ca. 10% langsamer.

Auf meinem 64bit AMD oder was ich da habe sieht die Sache wieder anders aus;
Alzheimer 9,709 ms
RetNyg Asm 9,467 ms
======================

Einigen wir uns auf: 3:3 für Dich! :beer:


uall@ogc - Mo 15.08.05 18:54


Delphi-Quelltext
1:
2:
  mn := mn shl 8;
  mn := mn shr 8;


wir wird gerade ganz schwindelig ;P (ok habs so auch irgendwann mal gemacht)


Delphi-Quelltext
1:
mn := mn and $0000FFFF;                    


ist dein freund
aber das nur nebenbei


retnyg - Mo 15.08.05 19:30

user profile iconuall@ogc hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
  mn := mn shl 8;
  mn := mn shr 8;


wir wird gerade ganz schwindelig ;P (ok habs so auch irgendwann mal gemacht)
hab ich im neuen asm code eh anders gelöst, das war genau das was die function schlussendlich schneller machte

user profile iconalzaimar hat folgendes geschrieben:
Poste mal die komplette Test-App, mir scheint es liegt an den Prozessoren.

bitteschön, brauchst nur jeweils bei button3click die funktion die du testen willst auskommentieren

Delphi-Quelltext
1:
2:
3:
4:
            S := '04-' + Month[K] + '-05';
            //S := asmConvertDate(S); // meine proc
            //s:=convertdatefastest(s);  // deine proc
            s:=spconvertdate3(s); // sprints proc


alzaimar - Mo 15.08.05 21:02

Ich habe das Testprogramm mal umgeschrieben, damit alle Funktionen hintereinander ablaufen.
Das liefert mein Laptop (PM 1,5 GHZ)

Quelltext
1:
2:
3:
4:
5:
6:
7:
ConvertDateFastest1   5367: 
ConvertDateFastest    5208: 
AsmConvertDate16bit   6059: 
AsmConvertDate        5618: 
sprintConvertDate     5297: 
spConvertDate3        5318: 
spConvertDate         5368:

Is schon lustig. Ach, egal. Nimmt sich sowieso alles nix.


retnyg - Mo 15.08.05 22:32

user profile iconalzaimar hat folgendes geschrieben:
Ich habe das Testprogramm mal umgeschrieben, damit alle Funktionen hintereinander ablaufen.
Das liefert mein Laptop (PM 1,5 GHZ)

das mein 3.0'er: (etwas schneller :twisted: )
Zitat:

ConvertDateFastest1 2125:
ConvertDateFastest 2125:
AsmConvertDate16bit 1938:
AsmConvertDate 1953:
sprintConvertDate 2031:
spConvertDate3 2047:
spConvertDate 2031:

ConvertDateFastest1 2125:
ConvertDateFastest 2125:
AsmConvertDate16bit 1937:
AsmConvertDate 1953:
sprintConvertDate 2032:
spConvertDate3 2046:
spConvertDate 2032:

user profile iconalzaimar hat folgendes geschrieben:
Is schon lustig. Ach, egal. Nimmt sich sowieso alles nix.

jo, das ist alles schon verdammt nah am optimum.
ich habe die funktionen übrigens drum nicht hintereinander laufen lassen, weil das die performance beeinflusst hat (selbe funktion war bei klcik auf nen anderen button ein stück langsamer)


Sprint - Di 16.08.05 07:06

So, hab mein Pascal Code noch mal optimiert.


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:
function ConvertDate(const ADate: String): AnsiString;
var
  P: PByte;
  M: Integer;
begin
  SetLength(Result, 8);
  PWordArray(Pointer(Result))[0] := PWordArray(Pointer(ADate))[0];
  PByteArray(Pointer(Result))[6] := PByteArray(Pointer(ADate))[7];
  PByteArray(Pointer(Result))[7] := PByteArray(Pointer(ADate))[8];
  PByteArray(Pointer(Result))[2] := Ord('.');
  PByteArray(Pointer(Result))[5] := Ord('.');
  P := Pointer(Integer(ADate) + 3);
  if PIntegerArray(P)[0] = $2D6E614A then M := 1
  else if PIntegerArray(P)[0] = $2D626546 then M := 2
  else if PIntegerArray(P)[0] = $2D72614D then M := 3
  else if PIntegerArray(P)[0] = $2D727041 then M := 4
  else if PIntegerArray(P)[0] = $2D79614D then M := 5
  else if PIntegerArray(P)[0] = $2D6E754A then M := 6
  else if PIntegerArray(P)[0] = $2D6C754A then M := 7
  else if PIntegerArray(P)[0] = $2D677541 then M := 8
  else if PIntegerArray(P)[0] = $2D706553 then M := 9
  else if PIntegerArray(P)[0] = $2D74634F then M := 10
  else if PIntegerArray(P)[0] = $2D766F4E then M := 11
  else M := 12;
  if M < 10 then
  begin
    PByteArray(Pointer(Result))[3] := Ord('0');
    PByteArray(Pointer(Result))[4] := Byte(Chr(48 + M));
  end else
  begin
    PByteArray(Pointer(Result))[3] := Ord('1');
    PByteArray(Pointer(Result))[4] := Byte(Chr(38 + M));
  end;
end;


Mit Delphi 7 auf meinem AMD sehe ich keinen Geschwindigkeitsunterschied zum ASM Code.



Edit: \\
In Assembler sieht's dann so aus:

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:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
function ConvertDate(const ADate: AnsiString): AnsiString;
asm
  push ebx
  push esi
  mov  ebx, edx
  mov  esi, eax
  mov  eax, ebx
  mov  edx, $00000008
  call System.@LStrSetLength
  mov  edx, esi
  mov  cx, [edx]
  mov  eax, [ebx]
  mov  [eax], cx
  mov  cl, [edx+$07]
  mov  [eax+$06], cl
  mov  dl, [edx+$08]
  mov  [eax+$07], dl
  mov  byte ptr [eax+$02], $2E
  mov  byte ptr [eax+$05], $2E
  mov  edx, esi
  add  edx, $03
  mov  ecx, [edx]
  cmp  ecx, $2D6E614A
  jnz  @1
  mov  edx, $00000001
  jmp  @12

@1:
  cmp  ecx, $2D626546
  jnz  @2
  mov  edx, $00000002
  jmp  @12

@2:
  cmp  ecx, $2D72614D
  jnz  @3
  mov  edx, $00000003
  jmp  @12

@3:
  cmp  ecx, $2D727041
  jnz  @4
  mov  edx, $00000004
  jmp  @12

@4:
  cmp  ecx, $2D79614D
  jnz  @5
  mov  edx, $00000005
  jmp  @12

@5:
  cmp  ecx, $2D6E754A
  jnz  @6
  mov  edx, $00000006
  jmp  @12

@6:
  cmp  ecx, $2D6C754A
  jnz  @7
  mov  edx, $00000007
  jmp  @12

@7:
  cmp  ecx, $2D677541
  jnz  @8
  mov  edx, $00000008
  jmp  @12

@8:
  cmp  ecx, $2D706553
  jnz  @9
  mov  edx, $00000009
  jmp  @12

@9:
  cmp  ecx, $2D74634F
  jnz  @10
  mov  edx, $0000000A
  jmp  @12

@10:
  cmp  ecx, $2D766F4E
  jnz  @11
  mov  edx, $0000000B
  jmp  @12

@11:
  mov  edx, $0000000C

@12:
  cmp  edx, $0A
  jnl  @13
  mov  byte ptr [eax+$03], $30
  add  edx, $30
  mov  [eax+$04], dl
  pop  esi
  pop  ebx
  ret

@13:
  mov  byte ptr [eax+$03], $31
  add  edx, $26
  mov  [eax+$04], dl
  pop  esi
  pop  ebx
end;


Sprint - Do 18.08.05 19:03

Meine Motivation lag in diesem Beitrag, dass ich mit der Pascal Syntax den Delphi Compiler dazu bewege, den selben Assembler Code zu erstellen wie es retnyg von Hand geschrieben hat. Das ist mir auch fast geglückt.
Naja, nun weiß ich wenigsten wo man die Case Anweisung in Records sinnvoll einsetzen kann. :wink:


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:
function ConvertDate(const ADate: AnsiString): AnsiString;
type
  PConvType = ^TConvType;
  TConvType = packed record
    case Byte of
      0: (A: Cardinal;
          B: Cardinal;);
      1: (C: array[0..4of Byte;
          D: Cardinal;);
      2: (E: array[0..2of Byte;
          F: Cardinal;);
      3: (G: Word;
          H: Cardinal;);
  end;
begin
  SetLength(Result, 8);
  PConvType(Pointer(Result)).A := PConvType(Pointer(ADate)).A;
  PConvType(Pointer(Result)).B := PConvType(Pointer(ADate)).D;
  if PConvType(Pointer(ADate)).F = Byte('J'or Byte('a'shl 8 or Byte('n'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('1'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('F'or Byte('e'shl 8 or Byte('b'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('2'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('M'or Byte('a'shl 8 or Byte('r'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('3'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('A'or Byte('p'shl 8 or Byte('r'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('4'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('M'or Byte('a'shl 8 or Byte('y'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('5'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('J'or Byte('u'shl 8 or Byte('n'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('6'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('J'or Byte('u'shl 8 or Byte('l'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('7'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('A'or Byte('u'shl 8 or Byte('g'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('8'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('S'or Byte('e'shl 8 or Byte('p'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('0'shl 8 or Byte('9'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('O'or Byte('c'shl 8 or Byte('t'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('1'shl 8 or Byte('0'shl 16 or Byte('.'shl 24
  else if PConvType(Pointer(ADate)).F = Byte('N'or Byte('o'shl 8 or Byte('v'shl 16 or Byte('-'shl 24 then
    PConvType(Pointer(Result)).H := Byte('.'or Byte('1'shl 8 or Byte('1'shl 16 or Byte('.'shl 24
  else
    PConvType(Pointer(Result)).H := Byte('.'or Byte('1'shl 8 or Byte('2'shl 16 or Byte('.'shl 24
end;


retnyg - Do 18.08.05 21:18

user profile iconSprint hat folgendes geschrieben:
Meine Motivation lag in diesem Beitrag, dass ich mit der Pascal Syntax den Delphi Compiler dazu bewege, den selben Assembler Code zu erstellen wie es retnyg von Hand geschrieben hat. Das ist mir auch fast geglückt.

in der tat ^^
user profile iconSprint hat folgendes geschrieben:
Naja, nun weiß ich wenigsten wo man die Case Anweisung in Records sinnvoll einsetzen kann. :wink:

verdammt gute idee mit dem case trick, der code ist nun auch schneller.
da hat der compiler wohl noch eine anweisung rausoptimiert.

er funktioniert aber leider nur wenn das datum mit '-' getrennt wurde (bei meinem ist es egal, und er nimmt das eingangstrennzeichen auch fürs result)