AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Stringoperationen :: Performancemessung am bsp ANSI->ASCII
Thema durchsuchen
Ansicht
Themen-Optionen

Stringoperationen :: Performancemessung am bsp ANSI->ASCII

Ein Thema von choose · begonnen am 5. Dez 2003 · letzter Beitrag vom 9. Dez 2003
Antwort Antwort
Seite 1 von 2  1 2      
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#1

Stringoperationen :: Performancemessung am bsp ANSI->ASCI

  Alt 5. Dez 2003, 17:19
Hallo X-Dragon,
ich beschreibe im Folgenden Messungen, die sich aus unserer Diskussion im Thread ASCII <-> ANSI ergaben, aber nichts mit der eigentlichen Fragegestellung zu tun haben.

Ausgangspunkt dieser Messung war die Frage nach einer performanteren Funktion, die eine veränderte Kopie eines Eingabestrings zurückgibt, etwa in dieser Form:
Delphi-Quellcode:
function GetModifiedString(const AnInputString: string): string;
var
  iChar: Integer;
begin
  Result:= '';
  for iChar:= 1 to Length(AnInputString) do
  case AnInputString[iChar] of
    ACharOne: Result:= Result+AnotherCharOne;
    ACharTwo: Result:= Result+AnotherCharTwo;
  else
    Result:= Result+AnInputString[iChar];
  end;
end;
Unter der Vorraussetzung, dass die Länge des Ergebnisstrings identisch mit der des Eingabestrings ist, sollte eine Lösung der Form
Delphi-Quellcode:
function GetModifiedString(const AnInputString: string): string;
var
  iChar: Integer;
begin
  Result:= AnInputString;
  for iChar:= Length(AnInputString) downto 1 do
  case Result[iChar] of
    ACharOne: Result[iChar]:= AnotherCharOne;
    ACharTwo: Result[iChar]:= AnotherCharTwo;
  end;
end;
ungleich performanter sein.
Die hier dargestellten Ergebnisse sollen das bestätigen.


Die im Folgenden diskutierte Referenzimplementierung verwendet die von X-Dragon dargestellte Implementierung als "Originalversion" (I) im Vergleich zu einer gemäß der Zielstellung der Messung angepssten Version (II). In Anlehnung an die erwähnte Problemstellung, wird der Durchsatz der Routinen an 131072 (2^17) zufälligen Strings der festen Länge 8 getestet und ausgewertet. Diese Messung wird im Beispiel 8 mal für die jeweilige Implementierung wiederholt, um den Effekt von Einflussgrößen wie Caching, Fragmentierung, etc. zu minimieren bzw. zu erkennen.

Delphi-Quellcode:
program Project1;
{$APPTYPE CONSOLE}
uses
  SysUtils,
  Windows;

type
  TANSIToASCIIFunc = function(const AnInputString: string): string;

//:Orginal implementation (I) by X-Dragon
function AnsiToIBMAscii(const s: string): string;
var
  i : Integer;
begin
  Result:= '';

  for i := 1 to Length(s) do
    case s[i] of
      #196: Result := Result + #142; // von Ä
      #214: Result := Result + #153; // von Ö
      #220: Result := Result + #154; // von Ü
      #228: Result := Result + #132; // von ä
      #246: Result := Result + #148; // von ö
      #252: Result := Result + #129; // von ü
      #223: Result := Result + #225; // von ß
    else
      Result := Result + s[i];
    end;
end;

//:Modified implementation (II)
function AnsiToIBMAsciiRefactored(const AnInput: string): string;
var
  iChar : Integer;
begin
  Result:= AnInput;
  for iChar := Length(Result) downto 1 do
    case Result[iChar] of
      #196: Result[iChar] := #142; // von Ä
      #214: Result[iChar] := #153; // von Ö
      #220: Result[iChar] := #154; // von Ü
      #228: Result[iChar] := #132; // von ä
      #246: Result[iChar] := #148; // von ö
      #252: Result[iChar] := #129; // von ü
      #223: Result[iChar] := #225; // von ß
    end;
end;

//:Tests performance of AFunc against strings of AnArray
procedure DoTest(const AMessage: string; const AnArray: array of string;
  const AFunc: TANSIToASCIIFunc);
var
  iRun: Integer;
  iString: Integer;
  iStartTime: Cardinal;
  iNeedTime: Cardinal;
begin
  // run test eight times
  // might reveal problems with fragmentation
  for iRun:= 0 to 7 do
  begin
    // retrieve start time (in msecs)
    iStartTime:= GetTickCount;
    // call ANSI->ASCII converter for every string in AnArray
    for iString:= Low(AnArray) to High(AnArray) do
      AFunc(AnArray[iString]);
    // calculate needed time (in msecs
    iNeedTime:= GetTickCount-iStartTime;

    // output result
    Writeln(Format('%s (Run %d): %d msec (%d per sec)',
      [AMessage, Succ(iRun), iNeedTime,
       Length(AnArray)*1000 div Succ(iNeedTime)]));
  end;
end;

//:Regression test on given functions against string array
procedure EnsureSameResults(const AStrings: array of string;
  const AFunctions: array of TANSIToASCIIFunc);
var
  iString: Integer;
  iFunc: Integer;
  myResult: string;
begin
  Assert( Length(AFunctions)>=1 );

  // test every string in array
  for iString:= Low(AStrings) to High(AStrings) do
  begin
    // store result of first function in array
    myResult:= AFunctions[0](AStrings[iString]);

    // test stored result against result of every other function in array
    for iFunc:= Succ(Low(AFunctions)) to High(AFunctions) do
      if myResult<>AFunctions[iFunc](AStrings[iString]) then
      begin
        Writeln(Format('ERROR: String(%d)="%s", myResult="%s", AFunctions[%d]="%s"',
          [iString, AStrings[iString], myResult, iFunc,
           AFunctions[iFunc](AStrings[iString])]));
        Readln;
        Halt;
      end;
  end;

  Writeln(Format('Every function (%d) returns same result ',
    [Length(AFunctions)]));
end;

//:Creates random string with length of ALen
function MakeRandomString(const ALen: Cardinal): string;
var
  iChar: Cardinal;
begin
  SetLength(Result, ALen);
  // use only "visible" chars (ASCII-Value>=Space-Value)
  for iChar:=1 to ALen do
    Result[iChar]:= Char(32+Random(224));
end;

var
  myStrings: array[0..131071] of string;
  i: Integer;
begin
  // fill array with random strings
  for i:= Low(myStrings) to High(myStrings) do
    myStrings[i]:= MakeRandomString(8);
  writeln(Format('Created %d random strings', [Length(myStrings)]));

  // test functions
  EnsureSameResults(myStrings, [AnsiToIBMAscii, AnsiToIBMAsciiRefactored]);

  // do test with original version
  DoTest('origin', myStrings, AnsiToIBMAscii);
  // do test with refactored version
  DoTest('refact', myStrings, AnsiToIBMAsciiRefactored);

  Readln;
end.
Die Ausgabe des Programms eines beliebigen Laufs auf einem AMD Athlon 1,2 GHz unter Win2K hatte diese Gestalt:
Code:
Created 131072 random strings
Every function (2) returns same result
origin (Run 1): 771 msec (169782 per sec)
origin (Run 2): 761 msec (172010 per sec)
origin (Run 3): 761 msec (172010 per sec)
origin (Run 4): 762 msec (171785 per sec)
origin (Run 5): 771 msec (169782 per sec)
origin (Run 6): 771 msec (169782 per sec)
origin (Run 7): 771 msec (169782 per sec)
origin (Run 8]: 761 msec (172010 per sec)
refact (Run 1): 80 msec (1618172 per sec)
refact (Run 2): 80 msec (1618172 per sec)
refact (Run 3): 70 msec (1846084 per sec)
refact (Run 4): 80 msec (1618172 per sec)
refact (Run 5): 70 msec (1846084 per sec)
refact (Run 6): 71 msec (1820444 per sec)
refact (Run 7): 90 msec (1440351 per sec)
refact (Run 8]: 70 msec (1846084 per sec)
Es ist zu erkennen, dass beide Varianten mit konstanter Geschwindigkeit arbeiten, also keine Probleme mit Speicherfragementierung oä auftreten (@X-Dragon: Die voreilige Äußerung zur Zunahme der Zeit bei (I) begründete sich in der Tatsache, dass bei jedem Aufruf an Result Konkateniert wurde), und (II) im Vergleich zu (I) 10 mal schneller arbeitet.
Eine Profiler-Analyse (inkl Debug-DCUs mit AQTime3) ergab, für die Unteraufrufe aus (I) und (II) Folgendes:
Code:
Routine Name | % Time w C | Hit Count
--------------+------------+----------      
LStrFromChar |      50,01 |  1015812
LStrCat      |      34,35 |  1048576
LStrClr      |       8,85 |   262144
(body only)  |       4,53 |   131072
LStrLen      |       2,26 |   131072

                    Ergebnisse für (I)



Routine Name | % Time w C | Hit Count
--------------+------------+---------
(body only)  |      43,18 |   131072
LStrAsg      |      41,22 |   131072
UniqueStringA |      15,33 |    32764
LStrLen      |       0,27 |   131072

                   Ergebnisse für (II)
Die Tabellen zeigen in der Spalte "% Time with Children" die "verbrachte Zeit" in Unteraufrufen bzw. dem Code der Routine selbst ("body only") sowie die Anzahl der Aufrufe bei 131072 zu verarbeitenen Strings.

Es ist deutlich zu erkennen, dass (I) den Großteil der Zeit (>95%) mit der Verarbeitung von Strings (Alloziierung, Kopieren, etc.) statt mit der Fallunterscheidung, der eigentlichen Programmlogik, zubringt. In (II) hingegen, wird über 40% der Zeit für die Programmlogik genutzt und auch die Funktionen zur Stringverarbeitung werden maximal einmal pro Aufruf von (II) verwendet. Die Lösung (I) hingegen verwendet selbige etwa achtmal häufiger, was der Länge der zu verarbeitenen Strings entspricht.

Die Testergebnisse zeigen, dass die Variante (II) bedingt durch effizientere Stringverarbeitung im Rahmen des Testszenarios etwa 10 mal schneller ist.
Strings der Länge 4096 konnten diesen Quotienten auf über 26 Erhöhen.

Ich hoffe, dass die Ergebnisse dieses Beitrags helfen können, Programme mit Stringverarbeitung performanter zu gestalten, ohne direkten Speicherzugriff, zB in Form von PChar oder ASM, verwenden zu müssen.

[edit=sakura] Leerzeichen eingefügt Mfg, sakura[/edit]
gruß, choose
  Mit Zitat antworten Zitat
Benutzerbild von X-Dragon
X-Dragon

Registriert seit: 14. Jan 2003
Ort: Schortens
289 Beiträge
 
Delphi 6 Enterprise
 
#2

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 5. Dez 2003, 19:06
Wow, so eine Abhandlung hatte ich nun nicht erwartet .

Ich hatte mich jetzt in meinem Programm erstmal für die "veraltete" Methode entschieden, da sie bisher am schnellsten war:
Delphi-Quellcode:
function ANSI2ASCII(AText:string):string;
const MaxLength = 255;
var PText : PChar;
begin
  PText:=StrAlloc(MaxLength);
  StrPCopy(PText,AText);
  {$IFDEF WIN32} 
  CharToOEM(PText,PText); {32Bit} 
  {$ELSE} 
  AnsiToOEM(PText,PText); {16Bit} 
  {$ENDIF} 
  Result:=StrPas(PText);
  StrDispose(PText);
end;
Und nach den letzten Messungen in meinem Programm, dauert die Umwandlung von:

ca. 230.000 Datensätze * 7 Felder * 12 Byte = 19.320.000 Byte ca. 1 Min 50 Sek.

Deine verbesserte Version kann ich jetzt leider erst am Montag testen. Wobei ich noch nicht genau weiß wie ich das man besten mache, denn die ausgelesen Daten wandel ich jetzt immer direkt um und schreibe sie gleich in die DB. Zuvor hab ich die Daten ja zuerst komplett ausgelesen und umgewandelt und dann erst in die DB geschrieben .

Und ich muss jetzt nur noch das da:
http://www.delphipraxis.net/internal...ct.php?t=14593
... in den Griff bekommen denn das unterbringen der 230.000 Datensätze an sich benötigt doch verhältnismäßig etwas mehr Zeit .
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#3

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 6. Dez 2003, 01:52
Hallo X-Dragon,

wenn ich Montag wieder im Bureau bin, könnte ich die Messung an Deiner zweiten Variante wiederholen (das "Test-Framework" ist ja nun fertig), sofern ich es bis dahin nicht vergessen habe
gruß, choose
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#4

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 09:31
Wie angekündigt habe ich das Testszenario nun um die beiden Implementierungen (III)
Delphi-Quellcode:
function AnsiToOEMStrAlloc(const AnInput: string): string;
var
  PText : PChar;
begin
  PText:=StrAlloc(Succ(UsedStrLen));
  try
    StrPCopy(PText, AnInput);
    CharToOEM(PText, PText);
    Result:= StrPas(PText);
  finally
    StrDispose(PText);
  end;
end;
vorgeschlagen von X-Dragon, sowie einer Lösung (IV), bei der die Delphi-Implementierung (ab Delphi3?) von Strings ausgenutzt wird, genauer die Tatsache, dass die Zuweisung myPChar:= PChar(myString) gültig ist, weil die Pascal-typschen Dinge wie Längeninformation und Referenzzähler bei negativen Offsets liegen, sowie der Tatsache, dass die Strings null-terminiert sind und CharToOEM die Länge des Strings nicht verändert:
Delphi-Quellcode:
function AnsiToOEMPChar(const AnInput: string): string;
begin
  SetLength(Result, Length(AnInput));
  CharToOEM(PAnsiChar(AnInput), PAnsiChar(Result));
end;
Mit 131072 Strings der Länge 8, die im Gegensatz zum vorherigen Test lediglich Zeichen des Alphabets sowie ein Leerzeichen und die ANSI-Umlaute und das ß enthalten, war bei einem beliebigen Testlauf folgendes Resultat mit (III=stralloc und IV=PChar) zu erkennen:
Code:
Created 131072 random strings (len: 8)
Each function (4) returns same result
origin  [Run 1]: 591 msec (221405 per sec)
origin  [Run 2]: 591 msec (221405 per sec)
origin  [Run 3]: 601 msec (217727 per sec)
origin  [Run 4]: 601 msec (217727 per sec)
origin  [Run 5]: 591 msec (221405 per sec)
origin  [Run 6]: 600 msec (218089 per sec)
origin  [Run 7]: 591 msec (221405 per sec)
origin  [Run 8]: 601 msec (217727 per sec)
refact  [Run 1]: 80 msec (1618172 per sec)
refact  [Run 2]: 70 msec (1846084 per sec)
refact  [Run 3]: 80 msec (1618172 per sec)
refact  [Run 4]: 71 msec (1820444 per sec)
refact  [Run 5]: 90 msec (1440351 per sec)
refact  [Run 6]: 80 msec (1618172 per sec)
refact  [Run 7]: 80 msec (1618172 per sec)
refact  [Run 8]: 90 msec (1440351 per sec)
stralloc [Run 1]: 160 msec (814111 per sec)
stralloc [Run 2]: 151 msec (862315 per sec)
stralloc [Run 3]: 130 msec (1000549 per sec)
stralloc [Run 4]: 140 msec (929588 per sec)
stralloc [Run 5]: 130 msec (1000549 per sec)
stralloc [Run 6]: 150 msec (868026 per sec)
stralloc [Run 7]: 161 msec (809086 per sec)
stralloc [Run 8]: 140 msec (929588 per sec)
PChar   [Run 1]: 50 msec (2570039 per sec)
PChar   [Run 2]: 40 msec (3196878 per sec)
PChar   [Run 3]: 50 msec (2570039 per sec)
PChar   [Run 4]: 40 msec (3196878 per sec)
PChar   [Run 5]: 50 msec (2570039 per sec)
PChar   [Run 6]: 40 msec (3196878 per sec)
PChar   [Run 7]: 50 msec (2570039 per sec)
PChar   [Run 8]: 40 msec (3196878 per sec)
deutlicher wird der Geschwindkeitsvorsprung von (IV) jedoch bei Strings größerer Länge (hier: 512 Zeichen):
Code:
Created 131072 random strings (len: 512)
Each function (4) returns same result
origin  [Run 1]: 33898 msec (3866 per sec)
origin  [Run 2]: 33899 msec (3866 per sec)
origin  [Run 3]: 33959 msec (3859 per sec)
origin  [Run 4]: 33889 msec (3867 per sec)
origin  [Run 5]: 34029 msec (3851 per sec)
origin  [Run 6]: 33898 msec (3866 per sec)
origin  [Run 7]: 33909 msec (3865 per sec)
origin  [Run 8]: 33769 msec (3881 per sec)
refact  [Run 1]: 1392 msec (94093 per sec)
refact  [Run 2]: 1372 msec (95463 per sec)
refact  [Run 3]: 1392 msec (94093 per sec)
refact  [Run 4]: 1392 msec (94093 per sec)
refact  [Run 5]: 1392 msec (94093 per sec)
refact  [Run 6]: 1392 msec (94093 per sec)
refact  [Run 7]: 1402 msec (93422 per sec)
refact  [Run 8]: 1392 msec (94093 per sec)
stralloc [Run 1]: 1161 msec (112798 per sec)
stralloc [Run 2]: 1172 msec (111740 per sec)
stralloc [Run 3]: 1162 msec (112701 per sec)
stralloc [Run 4]: 1161 msec (112798 per sec)
stralloc [Run 5]: 1162 msec (112701 per sec)
stralloc [Run 6]: 1152 msec (113679 per sec)
stralloc [Run 7]: 1161 msec (112798 per sec)
stralloc [Run 8]: 1172 msec (111740 per sec)
PChar   [Run 1]: 601 msec (217727 per sec)
PChar   [Run 2]: 591 msec (221405 per sec)
PChar   [Run 3]: 591 msec (221405 per sec)
PChar   [Run 4]: 601 msec (217727 per sec)
PChar   [Run 5]: 590 msec (221780 per sec)
PChar   [Run 6]: 601 msec (217727 per sec)
PChar   [Run 7]: 601 msec (217727 per sec)
PChar   [Run 8]: 591 msec (221405 per sec)
Die Darstellung der Unteraufrufe sieht bei den beiden Varianten (III) und (IV) wie folgt aus:
Code:
Routine Name | % Time w C | Hit Count
--------------+------------+----------
StrPas       |      43,93 |   131072
StrAlloc     |      13,32 |   131072
StrDispose   |      12,34 |   131072
body only    |      11,51 |   131072
CharToOem    |      11,36 |   131072
StrPCopy     |       7,54 |   131072

             Ergebnisse für (stralloc)


Routine Name | % Time w C | Hit Count
--------------+------------+----------
LStrSetLength |      69,87 |   131072
CharToOem    |      20,91 |   131072
body only    |       6,35 |   131072
LStrLen      |       2,87 |   131072

                Ergebnisse für (PChar)
Die Tests und Profilerläufe sind unter ähnlichen Bedingungen wie beim letzten Posting durchgeführt worden und stellen die Variante (IV) als die performanteste Variante dar.

Zur Erstellung der Ergebnisse habe ich den engehängten Quelltext unter D7 verwendet.
Angehängte Dateien
Dateityp: dpr project1_171.dpr (9,7 KB, 7x aufgerufen)
gruß, choose
  Mit Zitat antworten Zitat
Benutzerbild von Sourcemaker
Sourcemaker

Registriert seit: 3. Sep 2003
Ort: Westoverledingen
264 Beiträge
 
Delphi 11 Alexandria
 
#5

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 10:01
Hi Choose,

klasse gemachte Abhandlung mit guten Ansätzen für zukünftige Performance-Steigerung.

Gruß

Frank
Frank
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#6

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 10:18
Teste mal folgendes:

Delphi-Quellcode:

function AnsiToIBMAscii(const Source: String): String;
var
  I: Integer;
  C: Char;
  D,S: PChar;
begin
  SetLength(Result, Length(Source));
  S := PChar(Source);
  D := PChar(Result);
  while true do
  begin
    C := S^;
    Inc(S);
    case C of
        #0: Break;
      #223: C := #225; // von ß
      #228: C := #132; // von ä
      #246: C := #148; // von ö
      #252: C := #129; // von ü
      #220: C := #154; // von Ü
      #196: C := #142; // von Ä
      #214: C := #153; // von Ö
    end;
    D^ := C;
    Inc(D);
  end;
end;
Gruß Hagen
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#7

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 11:19
Hallo Hagen,

gerne Teste auch auch ein bisschen Pointer-Arithmetik (nun immerhin Inkrementieren eines Pointers), allerdings wollte ich ursprünglich eine performante Lösung ohne ASM und Pointer finden.
Der Vergleich mit dem bisherigen Favoriten (IV=PChar) und Deiner Implementierung (V=PArithm) bei dem geforderten Szenario
Code:
Created 131072 random strings (len: 8)
Each function (5) returns same result
PChar   [Run 1]: 50 msec (2570039 per sec)
PChar   [Run 2]: 30 msec (4228129 per sec)
PChar   [Run 3]: 50 msec (2570039 per sec)
PChar   [Run 4]: 30 msec (4228129 per sec)
PChar   [Run 5]: 30 msec (4228129 per sec)
PChar   [Run 6]: 50 msec (2570039 per sec)
PChar   [Run 7]: 30 msec (4228129 per sec)
PChar   [Run 8]: 50 msec (2570039 per sec)
PArithm [Run 1]: 40 msec (3196878 per sec)
PArithm [Run 2]: 51 msec (2520615 per sec)
PArithm [Run 3]: 50 msec (2570039 per sec)
PArithm [Run 4]: 50 msec (2570039 per sec)
PArithm [Run 5]: 40 msec (3196878 per sec)
PArithm [Run 6]: 60 msec (2148721 per sec)
PArithm [Run 7]: 40 msec (3196878 per sec)
PArithm [Run 8]: 50 msec (2570039 per sec)
sowie Strings der Länge 512
Code:
Created 131072 random strings (len: 512)
Each function (5) returns same result
PChar   [Run 1]: 591 msec (221405 per sec)
PChar   [Run 2]: 681 msec (192187 per sec)
PChar   [Run 3]: 651 msec (201030 per sec)
PChar   [Run 4]: 641 msec (204161 per sec)
PChar   [Run 5]: 601 msec (217727 per sec)
PChar   [Run 6]: 631 msec (207392 per sec)
PChar   [Run 7]: 611 msec (214169 per sec)
PChar   [Run 8]: 661 msec (197993 per sec)
PArithm [Run 1]: 1281 msec (102240 per sec)
PArithm [Run 2]: 1252 msec (104606 per sec)
PArithm [Run 3]: 1302 msec (100592 per sec)
PArithm [Run 4]: 1452 msec (90207 per sec)
PArithm [Run 5]: 1382 msec (94773 per sec)
PArithm [Run 6]: 1612 msec (81259 per sec)
PArithm [Run 7]: 1523 msec (86005 per sec)
PArithm [Run 8]: 1422 msec (92109 per sec)
Die Aufrufe innerhalb der Funktion (V) gestalten sich wie folgt
Code:
Routine Name | % Time w C | Hit Count
--------------+------------+----------
body only    |      54,58 |   131072
LStrSetLength |      33,41 |   131072
LStrLen      |      12,01 |   131072

              Ergebnisse für (PArithm)
Anzumerken ist, dass die Lösung mithilfe der Pointer-Arithmetik zwar langsamer als Variante (IV) ist, allerdings ist sie ohne Zuhilfenahme von API-Funktionen realisiert. Die Größenordnung der Geschwindkeit liegt bei der Lösung (II), was nicht weiter verwundert, weil der Delphi-Compiler Code erzeugt, der direkt mit den Offsets arbeitet (displacement über EAX und EBX) und nur unwesentlich durch den Aufruf von UniqueString verlangsamt wird. Sollte also eine ähnliche Problemstellung vorliegen, für die keine optimierte API-Funktion vorhanden ist, scheint diese etwas schnellere Version (V) oder die leicht übersichtlichere Variante (II) die bisher beste Wahl zu sein.

Vielleicht möchte noch jemand eine stark optimierte ASM-Version einreichen (zB Einlesen eines DWords und der Prämisse, dass es sich um null-terminierte Strings handelt), damit wir die Ergebnisse des Threads komplettieren können?
gruß, choose
  Mit Zitat antworten Zitat
Robert Marquardt
(Gast)

n/a Beiträge
 
#8

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 12:32
Das "case" sollte rausfliegen und durch eine Tabelle ersetzt werden.
  Mit Zitat antworten Zitat
choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#9

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 13:47
Hallo Robert,

ich habe Deinen Vorschlag als pure Pascal-Lösung (VI) implementiert:
Delphi-Quellcode:
function AnsiToAsciiCharMapping(const AnInput: string): string;
var
  iChar: Integer;
begin
  SetLength(Result, Length(AnInput));
  for iChar:= Length(Result) downto 1 do
    Result[iChar]:= CharMap[Result[iChar]];
end;
wobei ich die Tabelle wg des "Test-Frameworks" leider nicht als Parameter übergeben kann.
Mit einer Signatur der Form
function AnsiToAsciiCharMapping(const AMap: TCharacterMap; const AnInput: string): string; wäre bei einer flexibleren Lösung im erzeugen Code demnach eine weitere Indirektion statt eines konstanten Displacements der Art
Code:
mov dl, [edx+CharMap]
vorhanden...

Die Ergebnisse des Laufes mit String der Länge 8 im Vergleich zu (IV) und (V)
Code:
Created 131072 random strings (len: 8)
Each function (3) returns same result
PChar    (IV) [Run 1]: 50 msec (2570039 per sec)
PChar    (IV) [Run 2]: 50 msec (2570039 per sec)
PChar    (IV) [Run 3]: 50 msec (2570039 per sec)
PChar    (IV) [Run 4]: 40 msec (3196878 per sec)
PChar    (IV) [Run 5]: 50 msec (2570039 per sec)
PChar    (IV) [Run 6]: 40 msec (3196878 per sec)
PChar    (IV) [Run 7]: 50 msec (2570039 per sec)
PChar    (IV) [Run 8]: 51 msec (2520615 per sec)
PArith    (V) [Run 1]: 50 msec (2570039 per sec)
PArith    (V) [Run 2]: 50 msec (2570039 per sec)
PArith    (V) [Run 3]: 60 msec (2148721 per sec)
PArith    (V) [Run 4]: 60 msec (2148721 per sec)
PArith    (V) [Run 5]: 50 msec (2570039 per sec)
PArith    (V) [Run 6]: 60 msec (2148721 per sec)
PArith    (V) [Run 7]: 60 msec (2148721 per sec)
PArith    (V) [Run 8]: 50 msec (2570039 per sec)
CMapping (VI) [Run 1]: 50 msec (2570039 per sec)
CMapping (VI) [Run 2]: 40 msec (3196878 per sec)
CMapping (VI) [Run 3]: 50 msec (2570039 per sec)
CMapping (VI) [Run 4]: 40 msec (3196878 per sec)
CMapping (VI) [Run 5]: 61 msec (2114064 per sec)
CMapping (VI) [Run 6]: 50 msec (2570039 per sec)
CMapping (VI) [Run 7]: 50 msec (2570039 per sec)
CMapping (VI) [Run 8]: 50 msec (2570039 per sec)
der wirkliche Geschwindikeitsvorsprung des Mappings gegenüber der bisher schnellsten Pascal-Lösung (V) kommt erst bei längeren Strings (hier: 512 Zeichen) zum Vorschein:
Code:
Created 131072 random strings (len: 512)
Each function (3) returns same result
PChar    (IV) [Run 1]: 631 msec (207392 per sec)
PChar    (IV) [Run 2]: 630 msec (207721 per sec)
PChar    (IV) [Run 3]: 641 msec (204161 per sec)
PChar    (IV) [Run 4]: 631 msec (207392 per sec)
PChar    (IV) [Run 5]: 641 msec (204161 per sec)
PChar    (IV) [Run 6]: 641 msec (204161 per sec)
PChar    (IV) [Run 7]: 631 msec (207392 per sec)
PChar    (IV) [Run 8]: 641 msec (204161 per sec)
PArith    (V) [Run 1]: 1472 msec (88983 per sec)
PArith    (V) [Run 2]: 1472 msec (88983 per sec)
PArith    (V) [Run 3]: 1472 msec (88983 per sec)
PArith    (V) [Run 4]: 1482 msec (88383 per sec)
PArith    (V) [Run 5]: 1463 msec (89530 per sec)
PArith    (V) [Run 6]: 1482 msec (88383 per sec)
PArith    (V) [Run 7]: 1472 msec (88983 per sec)
PArith    (V) [Run 8]: 1462 msec (89591 per sec)
CMapping (VI) [Run 1]: 1031 msec (127007 per sec)
CMapping (VI) [Run 2]: 1032 msec (126884 per sec)
CMapping (VI) [Run 3]: 1021 msec (128250 per sec)
CMapping (VI) [Run 4]: 1032 msec (126884 per sec)
CMapping (VI) [Run 5]: 1021 msec (128250 per sec)
CMapping (VI) [Run 6]: 1022 msec (128125 per sec)
CMapping (VI) [Run 7]: 1031 msec (127007 per sec)
CMapping (VI) [Run 8]: 1042 msec (125668 per sec)
Die Ergebnisse der Unteraufrufe
Code:
Routine Name | % Time w C | Hit Count
--------------+------------+----------
UniqueStringA |      52,42 |  1048576
body only    |      22,19 |   131072
LStrSetLength |      20,29 |   131072
LStrLen      |       5,09 |   262144

             Ergebnisse für (CMapping)
deuten an, dass eine Lösung ohne die String-Implementierung von Delphi (zB mit PChar bzw ASM) das Verfahren deutlich beschleuningen sollte!

Damit ist die Lösung (VI) mit einer Tabelle die bisher effizienteste bei Problemstellungen, zu denen es keine API-Funktionen gibt. Darüber hinaus offeriert sie den Charm, einer gänzlich flexiblen Lösung: Zeichenrelation selbst kann vollständig zur Laufzeit erstellt werden.
gruß, choose
  Mit Zitat antworten Zitat
Robert Marquardt
(Gast)

n/a Beiträge
 
#10

Re: Stringoperationen :: Performancemessung am bsp ANSI->

  Alt 8. Dez 2003, 18:01
Wusst ichs doch
Ich bezweifle das eine ASM-Loesung lohnenswert schneller als eine PChar-Loesung in Pascal ist.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:00 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz