Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Auslesen eines MemoryStreams bis zum Zeilenumbruch


Heiko - Mi 21.09.05 16:25
Titel: Auslesen eines MemoryStreams bis zum Zeilenumbruch
Der Titel sagt ja eigentlich schon alles ;): Wie kann man am schnellsten den String bis zum dem nächsten Zeilenumbruch auslesen? Bisher mache ich es immer so, dass ich jedes Zeichen einzelnd auf den Zeilenumbruch überprüfe. Geht das auch effektiver?


Grishnak - Mi 21.09.05 16:36

String oder Stream?

bei String: Pos()


Heiko - Mi 21.09.05 16:40

Ne, bei einem Stream. Und mit Pos meinste, das ich nach #13#10 suchen soll ;).


BenBE - Mi 21.09.05 17:30

Du kannst ein Stream Buffering verwenden:


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:
var
    Len: Integer;
    Remaining: Integer;
    C: PChar;
    Data: String;
begin
    Len := 0;
    Remaining := MS.Size - MS.Position;
    
    C := PChar(Integer(MS.Memory) + MS.Position);
    While (Len < Remaining) and not (C^ in [#13#10]) Do 
    Begin
        Inc(C);
        Inc(Len);
    end;

    SetLength(Data, Len);
    MS.ReadBuffer(Data[1], Len);

    If not IsBadReadPtr(C, 1AND (C^ = #13Then
    Begin
        Inc(C^);
        If not IsBadReadPtr(C, 1AND (C^ = #10Then
            MS.Seek(2, soCurrent)
        Else
            MS.Seek(1, soCurrent);
    end
    else
        MS.Seek(1, soCurrent);
end;


Ungetestet.


Horst_H - Mi 21.09.05 20:11

Hallo,

einfach in eine Stringliste packen.
Das ist so verblueffend schnell.

Delphi-Quelltext
1:
2:
3:
4:
5:
StrL :Tstringlist;

StrL.create;
Strl.LoadFromStream(memstream);
...


Gruss Horst

Moderiert von user profile iconKlabautermann: Code- durch Delphi-Tags ersetzt


Heiko - Mi 21.09.05 20:22

Ich glaube nicht, dass es wirklich Performancevorteile bietet (ist zu einfach ;) ), da StringListen eigentlich nicht so für gute Performance bekannt ist ;). Trotzdem thx für die Idee.


Horst_H - Mi 21.09.05 23:22

Hallo,

ich habe mal code-Schnipseln aus http://dennishomepage.gugs-cats.dk/FastCodeProject.htm wie zum Beispiel PosEX bzw.PosEy (ein char suchen) gut verwenden koennen.
CharPosEY:
fuer Athlon und P4 das schnellste:
Aus http://dennishomepage.gugs-cats.dk/CharPosEYBV12.zip

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:
function CharPosEY_JOH_IA32_2_d(const SearchCharacter : Char;
                              const SourceString : AnsiString;
                              Occurrence : Integer = 1;
                              StartPos   : Integer = 1): Integer;
asm
  push   ebx
  push   edi
  push   edx                   {@SourceString}
  test   edx, edx
  jz     @@NotFound            {Exit if SourceString = ''}
  sub    ecx, 1                {Occurrence - 1}
  jl     @@NotFound            {Exit if Occurence < 1}
  cmp    StartPos, 1
  jl     @@NotFound            {Exit if StartPos < 1}
  mov    ebx, [edx-4]          {Length(SourceString)}
  lea    ebx, [edx+ebx-1]      {Last Character Position}
  mov    edx, StartPos
  add    edx, [esp]            {@SourceString}
  sub    edx, 1                {@SourceString + StartPos - 1}
  jmp    @@CheckFinish
@@Loop:
  cmp    al, [edx]
  jne    @@Done1
  sub    ecx, 1                {Match}
  jc     @@SetResult1
@@Done1:
  cmp    al, [edx+1]
  jne    @@Done2
  sub    ecx, 1                {Match}
  jnc    @@Done2
  cmp    edx, ebx
  jge    @@NotFound
  jmp    @@SetResult2
@@Done2:
  cmp    al, [edx+2]
  jne    @@Done3
  sub    ecx, 1                {Match}
  jnc    @@Done3
  cmp    edx, ebx
  jg     @@NotFound
  jmp    @@SetResult3
@@Done3:
  cmp    al, [edx+3]
  jne    @@Done4
  sub    ecx, 1                {Match}
  jnc    @@Done4
  add    edx, 3
  cmp    edx, ebx
  jle    @@SetResult1
@@Done4:
  add    edx, 4
@@CheckFinish:
  lea    edi, [edx+ecx]
  cmp    edi, ebx
  jle    @@Loop
@@NotFound:
  xor    eax, eax
  pop    edx
  jmp    @@Exit;
@@SetResult3:
  add    edx, 1
@@SetResult2:
  add    edx, 1
@@SetResult1:
  pop    ecx                   {@SourceString}
  neg    ecx                     
  lea    eax, [edx+ecx+1]
@@Exit:
  pop    edi
  pop    ebx
end;



Vielleicht ist das genau was Du suchst.

Gruss Horst
P.S.:
Hast Du mal Stringlisten probiert?

Moderiert von user profile iconMotzi: Code- durch Delphi-Tags ersetzt


Horst_H - Di 27.09.05 16:44

Hallo,

ich habe es jetzt mal getestest:

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:
procedure TForm1.Button1Click(Sender: TObject);
var
  T0,T1,T2,T3,T4,Tf: int64;
  i,StartPos,EndPos : Cardinal;
  l : integer;
  pCr : pchar;
begin
  ms:= TMemoryStream.Create;
  ms.Clear;
  If FileExists(Edit1.Text) then
     begin
     queryperformancefrequency(Tf);
     queryperformancecounter(T0);
     ms.LoadFromFile(Edit1.text);
     queryperformancecounter(T1);
     setlength(s,ms.size);
     ms.Position := 0;
     ms.ReadBuffer(s[1],ms.size);
     queryperformancecounter(T2);
     memo1.Lines.Text:=s;
     queryperformancecounter(T3);
     pCr := ms.memory;
     i := Cardinal(pCr);
     EndPos :=i+ms.Size;
     StartPos:= i;

     repeat
       if pChar(i)^ = #13 then
         begin
         inc(i);
         if pChar(i)^ =#10 then
           begin
           l := i-Startpos-1;
           setlength(s,l);
           move(pChar(Startpos)^,S[1],l);
           {Verarbeiten...}
           inc(i);
           startPos := i;
           end;
         end;
       inc(i)
     until i >EndPos;
     queryperformancecounter(T4);
     memo1.Lines.Add('');
     memo1.Lines.Add(s);
     s :=Format('dT1 %10.6e dt2 %10.6e dt3 %10.6e dt4 %10.6e Groesse : %10d',[ms.Size/((t1-t0)/tf),ms.Size/((t2-t1)/tf),ms.Size/((t3-t2)/tf),ms.Size/((t4-t3)/tf),ms.size]);
     memo1.Lines.Add(s);
     end;
  ms.Free;
end;
{
Ergebnis fuer eine 91671 und 1399 Zeilen (Bootlog.txt)
dT1 1,85810E+008 dt2 5,83880E+008 dt3 3,52691E+006 dt4 1,20773E+008 Groesse :      91671}

also grob:
Einlesen Stringstream 185 Mb/s (Cached)
in String kopieren 583 Mb/s
in Stringlist packen 3.5 Mb/s
nach und nach in String packen 120 Mb/s

Eine 2.6 Mb Datei mit 38863 Zeilen
dT1 3,60808E+008
dt2 2,40895E+008
dt3 5,93778E+006
dt4 1,40882E+008
Groesse : 2612155

Es ist schon ein gewaltiger Unterschied.
Also im Stream lassen ist das mit Abstand schnellste.
Aber stringlist ist nun mal einfacher in der Handhabung aber ueber 40 mal langsamer.

Gruss Horst