[delphi] Sneller met strings werken

Pagina: 1
Acties:

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
In het programma dat ik aan het ontwikkelen ben, maak ik veel gebruik van strings en de bijbehorende functies Pos en Copy. Deze bewerkingen duren echter 8 sec. op een 266mHz en dat vind ik te lang. Is er een manier om Delphi in te stellen om beter te werken met strings? Kunnen threads nuttig zijn? (Zo ja, hoe dan precies?) Of is er ergens een unit die dit werk efficienter kan doen?

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Er is niks dat sneller wordt van multithreading (iig in dit geval ;) ), tenzij er meer cpu's beschikbaar zijn.

Waarschijnlijk moet je het aantal copies beperken en/of de bewerkingen op een lager type zien te krijgen (bijvoorbeeld een character-array, maar mijn delphi kennis laat me niet toe daarop een antwoord te bedenken ;) )

  • marenk_vos
  • Registratie: Augustus 2001
  • Laatst online: 23-07 13:21
het lijkt me niet dat een pos en copy bewerking 8 sec. duurt. ik gebruik de pos functie zelf om strings waar een punt in staat te vervangen door een comma en het vervolgens om te zetten naar float. Dit gebeurd om een pentium 1 233 mhz en duurt nog geen seconde.

Dell XPS 17 Intel i7 2630 Nvidia GT555 3Gb Hd1: OCZ Vertex 2 SSD 60 GB Hd2: 500Gb


Verwijderd

Het is idd helaas waar dat strings in delphi sneller kunnen dan zoals ze nu geimplementeerd zijn. :'(

De assembly routines die dit zouden moeten verzorgen zijn niet geoptimaliseerd voor elke omstandigheid.

Hier wordt wat meer uitgelegd over hoe je dit kan optimaliseren.
http://www.borland.com/delphi/news/delphi_developer/optimizing.html

Hier kun je de snellere libs downen:
http://www.stuckindoors.com/delphi/ :)

succes

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op donderdag 18 april 2002 19:52 schreef marenk_vos het volgende:
het lijkt me niet dat een pos en copy bewerking 8 sec. duurt. ik gebruik de pos functie zelf om strings waar een punt in staat te vervangen door een comma en het vervolgens om te zetten naar float. Dit gebeurd om een pentium 1 233 mhz en duurt nog geen seconde.
Het zijn meerdere (namelijk 100x) Pos- en Copy-bewerkingen.

Verwijderd

Op donderdag 18 april 2002 20:44 schreef SiHiDa het volgende:
Het zijn meerdere (namelijk 100x) Pos- en Copy-bewerkingen.
Je hebt er wel al 'n basis optimimalisatie over heen gehaald zoals niet meerderekeren met pos het zelfde tekentje in de zelfde string opzoeken ensow?

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op donderdag 18 april 2002 20:07 schreef Materie het volgende:
Het is idd helaas waar dat strings in delphi sneller kunnen dan zoals ze nu geimplementeerd zijn. :'(

De assembly routines die dit zouden moeten verzorgen zijn niet geoptimaliseerd voor elke omstandigheid.

Hier wordt wat meer uitgelegd over hoe je dit kan optimaliseren.
http://www.borland.com/delphi/news/delphi_developer/optimizing.html

Hier kun je de snellere libs downen:
http://www.stuckindoors.com/delphi/ :)

succes
Je bedoelt waarschijnlijk dit, he: FastStrings. Dit ziet er goed uit. :) Heeft iemand hier ervaring mee?

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:25

Creepy

Tactical Espionage Splatterer

Op donderdag 18 april 2002 20:44 schreef SiHiDa het volgende:

[..]

Het zijn meerdere (namelijk 100x) Pos- en Copy-bewerkingen.
100x? Of 1000+ keer? Want 8 seconden lijkt me wel HEEL erg lang.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Knutselsmurf
  • Registratie: December 2000
  • Nu online

Knutselsmurf

LED's make things better

Ik weet niet wat je precies wilt doen met pos en copy, maar misschien is de stringreplace() functie een mogelijkheid. Hangt er natuurlijk wel vanaf wat je wilt doen. Misschien kun je daar wat duidelijkheid over geven?

- This line is intentionally left blank -


  • Orphix
  • Registratie: Februari 2000
  • Niet online
geef eens een stukje code, want in 8 sec kan je echt heeeel veel strings bewerken.

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Okee, waar ik het stukje voor nodig heb (op veler verzoek):

Ik ben bezig met het ontwikkelen van een GoT-Browser. Daarvoor wil ik graag uit een topiclist de titles, ID's, aantal Replies, enz. halen en deze in een soort van Listview plakken (voor de geinteresseerden ElTree uit het ElPack). Daarvoor gebruik ik de volgende code: (ik weet het, het is heel omslachtig, maar ik heb te weinig verstand van en Delphi en van de manier waarop HTML/GoT werkt.)
code:
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
var
Teller,Teller2,PreItem,Positie,HulpPositie,Kleur:integer;
  Inhoud,HulpString,TopicID,Titel,DateTime,Replies,Starter:string;
  Ms:TMemoryStream;
  Str:TStringList;
  ListItem:TElTreeItem;
  TopicList:array of string;
begin
  PreItem:=ComboBox1.ItemIndex;
  if (Ini.ReadBool('Connectie','ConPag',False)) and (LoginState=True) and (CookieGeldig=False) then
  begin
    CookieNMGeldig;
    if PreItem in [23..27] then
    Exit;
  end;
  Button1.Enabled:=False;
  HttpCli1.Cookie:=Cookie;
  HttpCli1.URL:=HaalLinkVanForum(ComboBox1);
  Ms:=TMemoryStream.Create;
  HttpCli1.RcvdStream:=Ms;
  HttpCli1.Get;
  Ms.Seek(0, 0);
  Str:=TStringList.Create;
  Str.LoadFromStream(Ms);
  for Teller:=0 to Str.Count-1 do
    for Teller2:=0 to Length(Str[Teller]) do
    begin
    HulpString:=Str[Teller];
    Inhoud:=Inhoud+HulpString[Teller2];
    end;
  HttpCli1.RcvdStream.Destroy;
  HttpCli1.RcvdStream := nil;
  ElTree1.Items.Clear;
  Positie:=Pos('topic(',Inhoud);
  Inhoud:=Copy(Inhoud,Positie,Length(Inhoud)-Positie);
  HulpPositie:=Pos('/SCRIPT',Inhoud);
  Inhoud:=Copy(Inhoud,1,HulpPositie);
  Positie:=1;
  HulpPositie:=Pos(#39+');',Inhoud);
  repeat
    SetLength(TopicList,Length(TopicList)+1);
    for Teller:=Positie+7 to HulpPositie-1 do
    TopicList[High(TopicList)]:=TopicList[High(TopicList)]+Inhoud[Teller];
    Inhoud:=Copy(Inhoud,HulpPositie+4,Length(Inhoud)-HulpPositie);
    Positie:=Pos('topic(',Inhoud);
    HulpPositie:=Pos(#39+');',Inhoud);
  until Positie=0;
  for Teller:=Low(TopicList) to High(TopicList) do
  begin
    HulpPositie:=Pos('/',TopicList[Teller]);
    TopicID:=Copy(TopicList[Teller],1,HulpPositie-1);
    Positie:=Pos(',',TopicList[Teller]);
    Kleur:=StrToInt(TopicList[Teller][Positie+1]);
    Positie:=Positie+6;
    repeat
    Titel:=Titel+TopicList[Teller][Positie];
    Positie:=Positie+1;
    until (TopicList[Teller][Positie]='<') or (TopicList[Teller][Positie]=#39);
    if TopicList[Teller][Positie]='<' then
    begin
    TopicList[Teller]:=Copy(TopicList[Teller],Positie,Length(TopicList[Teller])-Positie);
    Positie:=Pos(#39,TopicList[Teller]);
    end;
    Positie:=Positie+3;
    repeat
    Starter:=Starter+TopicList[Teller][Positie];
    Positie:=Positie+1;
    until TopicList[Teller][Positie]=#39;
    Positie:=Positie+2;
    repeat
    Replies:=Replies+TopicList[Teller][Positie];
    Positie:=Positie+1;
    until TopicList[Teller][Positie]=',';
    for Teller2:=Positie+2 to Length(TopicList[Teller]) do
    DateTime:=DateTime+TopicList[Teller][Teller2];
    ListItem:=ElTree1.Items.AddItem(ElTree1.Items[ElTree1.Items.Count]);
    ListItem.Text:=Titel;
    ListItem.ColumnText.Add(TopicID);
    ListItem.ColumnText.Add(Replies);
    ListItem.ColumnText.Add(Starter);
    if (DateTime<> '') and (DateTime<>' ') then
    ListItem.ColumnText.Add(DateTime);
    if Kleur=8 then
    ListItem.ImageIndex:=4;
    if Kleur=5 then
    ListItem.ImageIndex:=3;
    if Kleur=4 then
    ListItem.ImageIndex:=2;
    if Kleur=3 then
    ListItem.ImageIndex:=1;
    if Kleur=2 then
    ListItem.ImageIndex:=0;
    TopicID:='';
    Replies:='';
    Starter:='';
    DateTime:='';
    Titel:='';
  end;
  Button1.Enabled:=True;

Ik weet het: het is niet erg overzichtelijk en er kan ipv 'if Kleur=4' ook een case-statement in, maar daar gaat het toch niet om? Misschien ook wel en is dat de remmende factor. Ik hoop dat ik nu duidelijk ben. (Als er een betere manier is om topics uit een topiclist te halen, vind ik dat natuurlijk ook best :) )

Verwijderd

Ik heb heel vluchtig je code bekeken en daar zijn zeer zeker vele zaken te verbeteren:
1) Probeer eerst alles in procedures/functies om te zetten i.p.v. één lange brei (een proc/functie met meer dan 10 regels is over het algemeen al onoverzichtelijk).
2) Houdt het aantal variabelen zo beperkt mogelijk; probeer maar eens de variabelen welke je slechts één keer gebruikt weg te laten en te vervangen door de echte functie (met name bij bijv. de functie Pos).
3) Vermijdt code als:
code:
1
2
while Pos(#39, sTxt)>0 do
  sTxt[Pos(#39, sTxt)]:=' ';

Dit kun je ook schrijven als:
code:
1
sTxt:=StringReplace(sTxt, #39, ' ', [rfReplaceAll]);

4) Je maakt gebruik van een stringlist en vervolgens lees je ieder item in. Dit kun je veel eenvoudiger doen met de volgende code:
code:
1
sTxt:=StringReplace(StrLst.Text, #13#10, '', [rfReplaceAll]);

5) Je gebruikt de functies Delete en Insert niet. Kijk daar eens naar.
6) Je gebruikt een dynamische array en verhoogt bij iedere iteratie de lengte van deze array. Dit neemt HEEL veel tijd in! Gebruik dan liever een TList o.i.d. met bijv. PChars.
7) Je gebruikt de Destroy i.p.v. Free. Hoewel dit niet fout is is het niet slim. Free gaat altijd goed, Destroy niet! Bij een TForm gebruik je Release btw. Vanaf Delphi 5 kun je ook FreeAndNil gebruiken om vrij te geven en meteen naar nil te assignen.
8) Je gebruikt LongStrings; Delphi kent ook ShortStrings. Hoewel deze maximaal 255 karakters lang is zijn ze wel sneller. Bovendien bevat karakter 0 van een shortstring de lengte van de shortstring zodat functies als Length niet nodig zijn.

Dit zijn zo 8 punten die ik er snel uit kon halen. Er is vast nog wel meer prestatie winst te behalen maar optimaliseer eerst zo je routines maar eens en dan ga je nog weer een stapje verder.

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op vrijdag 19 april 2002 16:04 schreef hvdberg het volgende:
Ik heb heel vluchtig je code bekeken en daar zijn zeer zeker vele zaken te verbeteren:
1) Probeer eerst alles in procedures/functies om te zetten i.p.v. één lange brei (een proc/functie met meer dan 10 regels is over het algemeen al onoverzichtelijk).
Heb je zeker gelijk in, maar voor de snelheid maakt het niet veel uit, lijkt me.
2) Houdt het aantal variabelen zo beperkt mogelijk; probeer maar eens de variabelen welke je slechts één keer gebruikt weg te laten en te vervangen door de echte functie (met name bij bijv. de functie Pos).
Maar is het niet sneller om de waarde van een functie die dan 3 keer achter elkaar moet worden uitgevoerd (en hetzelfde resultaat geeft) aan een variable toe te kennen? 1 variabele minder of meer betekent toch niet veel?
3) Vermijdt code als:
code:
1
2
while Pos(#39, sTxt)>0 do
  sTxt[Pos(#39, sTxt)]:=' ';

Dit kun je ook schrijven als:
code:
1
sTxt:=StringReplace(sTxt, #39, ' ', [rfReplaceAll]);
Maar nog steeds geen tijdswinst...
4) Je maakt gebruik van een stringlist en vervolgens lees je ieder item in. Dit kun je veel eenvoudiger doen met de volgende code:
code:
1
sTxt:=StringReplace(StrLst.Text, #13#10, '', [rfReplaceAll]);
Misschien eenvoudiger, maar in dit onderdeel van de code zit de belemmering niet. Hier gaat hij binnen 10 ms doorheen.
5) Je gebruikt de functies Delete en Insert niet. Kijk daar eens naar.
Met betrekking tot wat?
6) Je gebruikt een dynamische array en verhoogt bij iedere iteratie de lengte van deze array. Dit neemt HEEL veel tijd in! Gebruik dan liever een TList o.i.d. met bijv. PChars.
Hier gaat hij ook binnen 10ms doorheen...
7) Je gebruikt de Destroy i.p.v. Free. Hoewel dit niet fout is is het niet slim. Free gaat altijd goed, Destroy niet! Bij een TForm gebruik je Release btw. Vanaf Delphi 5 kun je ook FreeAndNil gebruiken om vrij te geven en meteen naar nil te assignen.
Weer: geen echte snelheidswinst...
8) Je gebruikt LongStrings; Delphi kent ook ShortStrings. Hoewel deze maximaal 255 karakters lang is zijn ze wel sneller. Bovendien bevat karakter 0 van een shortstring de lengte van de shortstring zodat functies als Length niet nodig zijn.
Heb het uitgeprobeerd, maar maakt niet veel uit.

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Probeer es te bepalen waar ie zo lang over doet dan, als hij met geen van deze genoemde optimalisaties versnelt moet het ergens anders in zitten, of niet :?

En 100x 10ms is natuurlijk wel 1 seconde ;)

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:25

Creepy

Tactical Espionage Splatterer

Op vrijdag 19 april 2002 19:11 schreef SiHiDa het volgende:

[..]

Heb je zeker gelijk in, maar voor de snelheid maakt het niet veel uit, lijkt me.
[..]
Maar het is dan wel makkelijker te bepalen in welke procedure/functie zoveel tijd wordt besteed en wat er dus geoptimaliseerd moet worden.
Maar is het niet sneller om de waarde van een functie die dan 3 keer achter elkaar moet worden uitgevoerd (en hetzelfde resultaat geeft) aan een variable toe te kennen? 1 variabele minder of meer betekent toch niet veel?
[..]

Maar nog steeds geen tijdswinst...
[..]
Weet je dat zeker? Heb je dat getest? Elke milliseconde is er 1 he.
Misschien eenvoudiger, maar in dit onderdeel van de code zit de belemmering niet. Hier gaat hij binnen 10 ms doorheen.
[..]
Zoals al gezegd: 100 keer 10 ms = 1 sec. Eenvoudigere code is ook eenvoudiger te optimaliseren.
Met betrekking tot wat?
[..]
Met die functies kan je stukken string uit een string halen. Dit is wat jij hier flink aan het doen bent, dus waarom zou ej zeniet gebruiken?
Hier gaat hij ook binnen 10ms doorheen...
[..]

Weer: geen echte snelheidswinst...
[..]
Geen echte?? Dus wel snelheids winst! Elke winst die je kan makkelijk kan pakken, pakken!
Heb het uitgeprobeerd, maar maakt niet veel uit.
Maar het maakt dus wel wat uit....

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Verwijderd

Als je de code vanaf
code:
1
ListItem:=ElTree1.Items.AddItem(ElTree1.Items[ElTree1.Items.Count]);

weg haalt, hoe snel doet hij er dan over?

Heeft die ElTree1 geen BeginUpdate en EndUpdate, en gebruik je die ook?

Het probleem zit dus misschien in het updaten van een visueel component.

Verwijderd

Ik heb een vb gemaakt hoe je met PChar's werkt; hiermee voorkom je kostbare code zoals
code:
1
Titel:=Titel+TopicList[Teller][Positie];

Dit is dus de laatste lus in jouw code; de code heb ik niet getest BTW :)
code:
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
var
  P, Q: PChar;
begin
  //HulpPositie := Pos('/', TopicList[Teller]);
  //TopicID := Copy(TopicList[Teller], 1, HulpPositie - 1);
  Q := PChar(TopicList[Teller]);
  P := StrScan(Q, '/');
  Assert(P <> nil, 'Geen / gevonden');
  SetLength(TopicID, P - Q);
  Move(Q^, PChar(TopicID)^, P - Q);
  
  //Positie := Pos(',', TopicList[Teller]);
  P := StrScan(Q, ',');
  Assert(P <> nil, 'Geen , gevonden');

  //Kleur := StrToInt(TopicList[Teller][Positie + 1]);
  Kleur := StrToInt((P + 1)^);

  //Positie := Positie + 6;
  Inc(P, 6);

  //repeat
  //  Titel := Titel + TopicList[Teller][Positie];
  //  Positie := Positie + 1;
  //until (TopicList[Teller][Positie] = '<') or 
  // (TopicList[Teller][Positie] = #39);
  { Met Q markeren we de startpositie }
  Q := P;
  Inc(P);
  while not (P^ in ['<', #39]) do
    Inc(P);
  SetLength(Titel, P - Q);
  Move(Q^, PChar(Titel)^, P - Q);

  //if TopicList[Teller][Positie] = '<' then
  //begin
  //  TopicList[Teller] := Copy(TopicList[Teller], Positie,
  //    Length(TopicList[Teller]) - Positie);
  //  Positie := Pos(#39, TopicList[Teller]);
  //end;
  { Als P al op een #39 staat, zal P niet veranderen }
  P := StrScan(P, #39);
  Assert(P <> nil, 'Geen #39 gevonden');

  //Positie := Positie + 3;
  Inc(P, 3);

  //repeat
  //  Starter := Starter + TopicList[Teller][Positie];
  //  Positie := Positie + 1;
  //until TopicList[Teller][Positie] = #39;
  Q := P;
  Inc(P);
  StrScan(P, #39);
  Assert(P <> nil, 'Geen #39 gevonden');
  SetLength(Starter, P - Q);
  Move(Q^, PChar(Starter)^, P - Q);

  //Positie := Positie + 2;
  Inc(P, 2);

  //repeat
  //  Replies := Replies + TopicList[Teller][Positie];
  //  Positie := Positie + 1;
  //until TopicList[Teller][Positie] = ',';
  Q := P;
  Inc(P);
  StrScan(P, ',');
  Assert(P <> nil, 'Geen , gevonden');
  SetLength(Replies, P - Q);
  Move(Q^, PChar(Replies)^, P - Q);

  //for Teller2 := Positie + 2 to Length(TopicList[Teller]) do
  //  DateTime := DateTime + TopicList[Teller][Teller2];
  Inc(P, 2);
  Q := P;
  StrScan(P, #0);
  Assert(P <> nil, 'Geen #0 gevonden');
  SetLength(DateTime, P - Q);
  Move(P^, PChar(DateTime)^, P - Q);
end;

edit:

P-Q-1 => P-Q

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op zaterdag 20 april 2002 14:09 schreef DiFool het volgende:
Als je de code vanaf
code:
1
ListItem:=ElTree1.Items.AddItem(ElTree1.Items[ElTree1.Items.Count]);

weg haalt, hoe snel doet hij er dan over?

Heeft die ElTree1 geen BeginUpdate en EndUpdate, en gebruik je die ook?

Het probleem zit dus misschien in het updaten van een visueel component.
Precies. :) Over HK doet de procedure, zonder toevoegen aan ElTree1, op een 1,2 gHz slechts 150 ms. Het zit puur in het updaten van het component, maar wat bedoel je precies met die BeginUpdate en EndUpdate?

Verwijderd

Veel visuele componenten hebben een BeginUpdate en een EndUpdate method; hiermee geef je aan dat je het component aan het updaten bent, en dat het component zichzelf in die tijd niet - bij elke aanpassing - moet hertekenen.

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op zaterdag 20 april 2002 14:54 schreef DiFool het volgende:
Veel visuele componenten hebben een BeginUpdate en een EndUpdate method; hiermee geef je aan dat je het component aan het updaten bent, en dat het component zichzelf in die tijd niet - bij elke aanpassing - moet hertekenen.
Thanx, dat wist ik niet. :) Ik zal nu alle dingen die in dit artikel genoemd zijn, uitproberen en dan meld ik hier de resultaten. Wordt vervolgd.

Verwijderd

Op zaterdag 20 april 2002 15:26 schreef SiHiDa het volgende:
Thanx, dat wist ik niet. :) Ik zal nu alle dingen die in dit artikel genoemd zijn, uitproberen en dan meld ik hier de resultaten. Wordt vervolgd.
Bij mij ging 't vullen van een TreeView na het gebruik van BeginUpdate/EndUpdate van 80 seconden naar 0,3 seconde.
Wel de moeite waard om als eerste naar te kijken, lijkt me. :)

  • SiHiDa
  • Registratie: December 2001
  • Laatst online: 01-03-2023
Op zaterdag 20 april 2002 18:36 schreef Afterlife het volgende:

[..]

Bij mij ging 't vullen van een TreeView na het gebruik van BeginUpdate/EndUpdate van 80 seconden naar 0,3 seconde.
Wel de moeite waard om als eerste naar te kijken, lijkt me. :)
Precies, de procedure is inmiddels al ruim 75% sneller. Ik ben tevreden. :)

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 06:09

Tomatoman

Fulltime prutser

Bij mij ging 't vullen van een TreeView na het gebruik van BeginUpdate/EndUpdate van 80 seconden naar 0,3 seconde.
Wel de moeite waard om als eerste naar te kijken, lijkt me.
Dat gaat ook op voor het leegmaken van een TreeView. BeginUpdate - Clear - EndUpdate in de OnClose van je form kan er soms voor voor zorgen dat je applicatie seconden sneller afsluit.

Een goede grap mag vrienden kosten.

Pagina: 1