Delphi-PRAXiS
Seite 4 von 4   « Erste     234   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Anzahl eines Zeichens im String ermitteln (https://www.delphipraxis.net/116372-anzahl-eines-zeichens-im-string-ermitteln.html)

KodeZwerg 14. Jul 2018 23:01

AW: Anzahl eines Zeichens im String ermitteln
 
Hier schau mal selbst, das ist mit nur 50000 Zeichen/Bytes, achte auf den Ydobon Zeitwert. Katastrophal bei mir.
Diese Ergebnisse sind nicht unter optimalen Voraussetzungen entstanden da alle Kerne mit was anderem ausgelastet sind.
Zitat:

KodeZwerg's custom Benchmark - DP CountCharInString Edition

Benchmark: Sleep(500)
501037 Nanoseconds wasted. As closer this is to 500000 more accurate Results are given.

Benchmark: Generate Random Data String (50000 chars) Charset: [aA0-zZ9]
609 Nanoseconds wasted.

Benchmark: 1234588 miep(Data, 'X')
141 Nanoseconds wasted. (found X = 748 times)

Benchmark: Ydobon Length(Data)-Length(StringReplace(Data, 'X', '', [rfReplaceAll]))
21149 Nanoseconds wasted. (found X = 748 times)

Benchmark: marabu Occurs(Data, 'X')
201 Nanoseconds wasted. (found X = 748 times)

Benchmark: Missionar cCount(Data, 'X')
139 Nanoseconds wasted. (found X = 748 times)

Benchmark: alzaimar IFCount(Data, 'X')
122 Nanoseconds wasted. (found X = 748 times)

Benchmark: Uwe Raabe StringCountChar(Data, 'X')
122 Nanoseconds wasted. (found X = 748 times)

Benchmark: KodeZwerg CountCharInString(Data, 'X')
160 Nanoseconds wasted. (found X = 748 times)

Benchmark: KodeZwerg CharInStringA(Data, 'X')
253 Nanoseconds wasted. (found X = 748 times)

Benchmark: Uwe Raabe CharCount(Data, 'X')
89 Nanoseconds wasted. (found X = 748 times)

Benchmark: Uwe Raabe CharCount2(Data, 'X')
58 Nanoseconds wasted. (found X = 748 times)

Benchmark: EgonHugeist CharCount3(Data, 'X')
74 Nanoseconds wasted. (found X = 748 times)

Benchmark: Neutral General CharCountAsm(Data, 'X', Length(Data))
115 Nanoseconds wasted. (found X = 748 times)

Benchmark: EgonHugeist EH_CharCount4(Data, 'X')
76 Nanoseconds wasted. (found X = 748 times)

Benchmark: StrScanFast(Data, 'X')
1 Nanoseconds wasted. (found X = 49895 times)

Benchmark: Generate Random Binary Data String (50000 bytes)
324 Nanoseconds wasted.

Benchmark: 1234588 miep(Data, 'X')
109 Nanoseconds wasted. (found X = 180 times)

Benchmark: Ydobon Length(Data)-Length(StringReplace(Data, 'X', '', [rfReplaceAll]))
5814 Nanoseconds wasted. (found X = 180 times)

Benchmark: marabu Occurs(Data, 'X')
204 Nanoseconds wasted. (found X = 180 times)

Benchmark: Missionar cCount(Data, 'X')
131 Nanoseconds wasted. (found X = 180 times)

Benchmark: alzaimar IFCount(Data, 'X')
108 Nanoseconds wasted. (found X = 180 times)

Benchmark: Uwe Raabe StringCountChar(Data, 'X')
109 Nanoseconds wasted. (found X = 180 times)

Benchmark: KodeZwerg CountCharInString(Data, 'X')
1 Nanoseconds wasted. (found X = 1 times)

Benchmark: KodeZwerg CharInStringA(Data, 'X')
71 Nanoseconds wasted. (found X = 1 times)

Benchmark: Uwe Raabe CharCount(Data, 'X')
1 Nanoseconds wasted. (found X = 1 times)

Benchmark: Uwe Raabe CharCount2(Data, 'X')
63 Nanoseconds wasted. (found X = 180 times)

Benchmark: EgonHugeist CharCount3(Data, 'X')
59 Nanoseconds wasted. (found X = 180 times)

Benchmark: Neutral General CharCountAsm(Data, 'X', Length(Data))
154 Nanoseconds wasted. (found X = 180 times)

Benchmark: EgonHugeist EH_CharCount4(Data, 'X')
67 Nanoseconds wasted. (found X = 180 times)

Benchmark: StrScanFast(Data, 'X')
1 Nanoseconds wasted. (found X = 75 times)

jaenicke 14. Jul 2018 23:50

AW: Anzahl eines Zeichens im String ermitteln
 
Delphi 2009 war ja die erste Version mit Unicode. Vermutlich war dort die Umsetzung von StringReplace schlicht noch nicht so gut.

KodeZwerg 15. Jul 2018 01:01

AW: Anzahl eines Zeichens im String ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
Anhang 49520So in etwa würde neue GUI aussehen.
Wem noch mehr sinnvolle Optionen einfallen, bitte melden.

Edit
Ps: Ein Cancel Button ist nun vorhanden :)

KodeZwerg 15. Jul 2018 01:41

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von jbg (Beitrag 1407285)
Dann möchte ich meine schnell zusammengeschrieben Assembler-Rountine auch noch ins Rennen werfen.

Delphi 2009 verweigert mir Deinen Code. Benötige ich dafür ein aktuelleres Delphi als 2009 oder fehlt mir was in Uses?
Delphi-Quellcode:
Windows, SysUtils;
habe ich momentan drinnen.
Zitat:

[DCC Error] uCharInString.pas(277): E2003 Undeclared identifier: 'rdi'
[DCC Error] uCharInString.pas(337): E2003 Undeclared identifier: 'rcx'
[DCC Error] uCharInString.pas(340): E2003 Undeclared identifier: 'r8'
[DCC Error] uCharInString.pas(341): E2003 Undeclared identifier: 'rax'
[DCC Error] uCharInString.pas(346): E2003 Undeclared identifier: 'movsxd'
[DCC Error] uCharInString.pas(349): E2003 Undeclared identifier: 'r9'

himitsu 15. Jul 2018 01:59

AW: Anzahl eines Zeichens im String ermitteln
 
Wollt ihr den String nicht auch noch aufteilen und Multithreaded mit SSE/MMX/... oder besser noch mit CUDA/OpenCL/... beackern?


Zitat:

[DCC Error] uCharInString.pas(277): E2003 Undeclared identifier: 'rdi'
[DCC Error] uCharInString.pas(337): E2003 Undeclared identifier: 'rcx'
[DCC Error] uCharInString.pas(340): E2003 Undeclared identifier: 'r8'
[DCC Error] uCharInString.pas(341): E2003 Undeclared identifier: 'rax'
[DCC Error] uCharInString.pas(346): E2003 Undeclared identifier: 'movsxd'
[DCC Error] uCharInString.pas(349): E2003 Undeclared identifier: 'r9'
2009 und 64 Bit :stupid::roll:

himitsu 15. Jul 2018 02:08

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407305)
Zitat:

Zitat von jbg (Beitrag 1407285)
Dann möchte ich meine schnell zusammengeschrieben Assembler-Rountine auch noch ins Rennen werfen.

Delphi 2009 verweigert mir Deinen Code. Benötige ich dafür ein aktuelleres Delphi als 2009 oder fehlt mir was in Uses?
Delphi-Quellcode:
Windows, SysUtils;
habe ich momentan drinnen.
Zitat:

[DCC Error] uCharInString.pas(277): E2003 Undeclared identifier: 'rdi'
[DCC Error] uCharInString.pas(337): E2003 Undeclared identifier: 'rcx'
[DCC Error] uCharInString.pas(340): E2003 Undeclared identifier: 'r8'
[DCC Error] uCharInString.pas(341): E2003 Undeclared identifier: 'rax'
[DCC Error] uCharInString.pas(346): E2003 Undeclared identifier: 'movsxd'
[DCC Error] uCharInString.pas(349): E2003 Undeclared identifier: 'r9'




PS: Wer viel misst misst Mist.
Gerade beim Selbstbau von Assemblerzeugs sollte man auch beachten, dass CPUs sich unterschiedlich verhalten.
Einige halbwegs Aktuelle planen z.B. schonmal vor und werden dann ausgebremst, wenn der Code sich anders/unvorhersehbar/abwechselnd verhält.
Ich hatte mal eine Zählschleife gebaut und bremste meine CPU so aus, dass die For-In-Schleife von Delphi doppelt so schnell lief. :stupid:

KodeZwerg 15. Jul 2018 05:55

AW: Anzahl eines Zeichens im String ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 2)
Anhang 49521 So sieht mein letzter Entwurf aus.
Vorkompiliert plus Source im Anhang.
Es sind noch nicht alle Varianten enthalten, aber bitte testet mal ob das einem Benchmark gerecht wird.

Abgesehen von noch fehlender Methoden brauche ich auch Hilfe bei etwas was mir gerade den Kopf verdreht.
Delphi-Quellcode:
 if ((Tests.ItemIndex = 0) or (Tests.ItemIndex = 1)) then
  begin
    for Loops := 0 to Times.Value do
    begin
      QueryPerformanceCounter(Int64((@lpPerformanceCount1)^));
      i := CharInString.miep(Data, Ch);
      QueryPerformanceCounter(Int64((@lpPerformanceCount2)^));
      QueryPerformanceFrequency(Int64((@Frequency)^));
      Curr := (1000000 * (lpPerformanceCount2.QuadPart - lpPerformanceCount1.QuadPart) / Frequency.QuadPart);
      if Loops = 0 then
      begin
        Last := Curr;
        Min := Curr;
        Max := Curr;
        Avg := Curr;
      end;
      if Curr < Min then Min := Curr;
      if Curr > Max then Max := Curr;
      if Curr < Last then Avg := (((Curr - Last) / 2) - Avg);
      if Curr > Last then Avg := (((Curr - Last) / 2) + Avg);
      Last := Curr;
      if fCancel = True then Break;
    end;
    Avg := Abs(Avg);
    if b = False then lstResults.Items.Add(format('%.10d',[Round(Avg)])+ ' - Statistic: [ASCII Data] ['+IntToStr(Round( Min + ((Max-Min)/2) ))+' Avg] ['+IntToStr(Round(Min))+' Min] ['+IntToStr(Round(Max))+' Max] [found ' + IntToStr(Ord(Ch)) + ' = '+IntToStr(i)+' times] @ Miep()');
    if b = True then lstResults.Items.Add(format('%.10d',[Round(Avg)])+ ' - Statistic: [Binary Data] ['+IntToStr(Round( Min + ((Max-Min)/2) ))+' Avg] ['+IntToStr(Round(Min))+' Min] ['+IntToStr(Round(Max))+' Max] [found ' + IntToStr(Ord(Ch)) + ' = '+IntToStr(i)+' times] @ Miep()');
  end;
  if fCancel = True then goto cancel;
So schaut nun ein kompletter Bench-Block aus, Hilfe brauche ich bei der Berechnung der Variable "Avg". Irgendwie mache ich da etwas falsch und komme gerade nicht auf die korrekte Lösung.
Die "Avg" Variable soll den praktischen Durchschnittswert darstellen wohingegen
Delphi-Quellcode:
['+IntToStr(Round( Min + ((Max-Min)/2) ))+' Avg]
den synthetischen Durchschnittswert darstellt.
Oder mache ich es bereits richtig? Mir dröhnt der Kopf. *Hilfe*

So hier sehen nun die Ergebnisse aus:
Zitat:

KodeZwerg's custom Benchmark - DP CountCharInString Edition

Benchmark: Sleep(500)
506229 Nanoseconds wasted. As closer this is to 500000 more accurate Results are given.

Benchmark: Generate Random Data String (500000 chars) Charset: [aA0-zZ9]
5977 Nanoseconds wasted.

0000000523 - Statistic: [ASCII Data] [645 Avg] [575 Min] [715 Max] [found 88 = 8068 times] @ Uwe Raabe CharCount2()
0000000764 - Statistic: [ASCII Data] [738 Avg] [724 Min] [751 Max] [found 88 = 8068 times] @ EgonHugeist EH_CharCount4()
0000000851 - Statistic: [ASCII Data] [667 Avg] [573 Min] [761 Max] [found 88 = 8068 times] @ EgonHugeist CharCount3()
0000000882 - Statistic: [ASCII Data] [602 Avg] [420 Min] [784 Max] [found 88 = 8068 times] @ EgonHugeist EH_CharCount_6()
0000001219 - Statistic: [ASCII Data] [1185 Avg] [1168 Min] [1202 Max] [found 88 = 8068 times] @ Uwe Raabe StringCountChar()
0000001222 - Statistic: [ASCII Data] [1107 Avg] [851 Min] [1363 Max] [found 88 = 8068 times] @ EgonHugeist EH_CharCount_5()
0000001352 - Statistic: [ASCII Data] [1165 Avg] [709 Min] [1620 Max] [found 88 = 8068 times] @ KodeZwerg CountCharInString()
0000001372 - Statistic: [ASCII Data] [1336 Avg] [1319 Min] [1354 Max] [found 88 = 8068 times] @ Uwe Raabe CharCount()
0000001517 - Statistic: [ASCII Data] [1426 Avg] [1161 Min] [1691 Max] [found 88 = 8068 times] @ Miep()
0000001559 - Statistic: [ASCII Data] [1423 Avg] [1168 Min] [1677 Max] [found 88 = 8068 times] @ alzaimar
0000001910 - Statistic: [ASCII Data] [1569 Avg] [1378 Min] [1760 Max] [found 88 = 8068 times] @ Missionar
0000002157 - Statistic: [ASCII Data] [1982 Avg] [1872 Min] [2092 Max] [found 88 = 8068 times] @ marabu
0000002400 - Statistic: [ASCII Data] [2062 Avg] [1889 Min] [2234 Max] [found 88 = 8068 times] @ KodeZwerg CharInStringA()
0000009962 - Statistic: [ASCII Data] [4643 Avg] [856 Min] [8430 Max] [found 88 = 8068 times] @ Neutral General CharCountAsm()
0012230425 - Statistic: [ASCII Data] [13141133 Avg] [12800203 Min] [13482064 Max] [found 88 = 8068 times] @ Ydobon
edit
Ps: Da es noch nicht Threaded kann man noch nicht Canceln !!!

jbg 15. Jul 2018 09:01

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407305)
Delphi 2009 verweigert mir Deinen Code. Benötige ich dafür ein aktuelleres Delphi als 2009 oder fehlt mir was in Uses?
Delphi-Quellcode:
Windows, SysUtils;
habe ich momentan drinnen.

Ach ja, da war doch was. Das CPUX86 DEFINE wurde ja erst später eingeführt, damit es mit dem CPUX64 harmoniert. Ersetze das
Delphi-Quellcode:
{$IFDEF CPUX86}
einfach durch ein
Delphi-Quellcode:
{$IFNDEF CPUX64}
(Das "N" nicht vergessen)

Uwe Raabe 15. Jul 2018 10:49

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407309)
So schaut nun ein kompletter Bench-Block aus,

Ich finde, du machst es dir hier unnötig kompliziert und erhöhst zumindest potentiell die Messungenauigkeit in dem du die Zeit für jede einzelne Iteration misst. In jedem Fall erhöhst du aber die Gesamtlaufzeit des Benchmarks (nicht das Ergebnis), weil innerhalb der Schleife ein Haufen berechnet wird (zur Zeit offenbar auch noch falsch), der da gar nicht nötig wäre (z.B. wird der von QueryPerformanceCounter gelieferte Wert bereits beim Windows-Start festgelegt).

Aktuell schaut deine Messung so aus:
Delphi-Quellcode:
      QueryPerformanceCounter(Int64((@lpPerformanceCount1)^));
      i := CharInString.miep(Data, Ch);
      QueryPerformanceCounter(Int64((@lpPerformanceCount2)^));
Die Auflösung von QueryPerformanceCounter liegt zwar unter 1 Mikrosekunde, aber deine Messungen liegen zum Teil deutlich unter diesem Wert. Das bedeutet, daß die gemessene Laufzeit im Bereich oder sogar unter der Messgenauigkeit liegt, was einen relativ hohen Messfehler, schlimmstenfalls sogar vollkommen unbrauchbare Ergebnisse erwarten lässt.

Diesen Messfehler kannst du sehr leicht reduzieren, wenn du die Messung über eine deutlich größere Anzahl der Aufrufe machst und dann durch diese Anzahl teilst. Die gemessene Zeit sollte also mindestens 2-3 Größenordnungen über der Messgenauigkeit liegen (das wären hier 0.01 - 0.1 ms).

Aktuell versuchst du die mehrfachen Einzelmessungen zu mitteln, was dir aber nur den Mittelwert der einzelnen Fehler liefert, diesen aber nicht zwingend verkleinert (z.B. wenn er immer positiv ist).

In meinem Benchmark hatte ich das schon so implementiert. Nach dem Start der Stopwatch wird die zu messende Funktion 10000x aufgerufen und dann die Gesamtzeit gemessen. Da hier nur die Rangfolge relevant ist, spare ich mir das Runterrechnen auf die mittlere Laufzeit eines einzelnen Aufrufs. Durch diese Maßnahme haben wir den obigen Messfehler auf 1/10000 verkleinert, womit er vernachlässigbar wird.

Nun kann man argumentieren, daß die Messung über die Schleife ja auch einen gewissen Overhead verursacht. Zu diesem Zweck mache ich einen Kalibrierungslauf mit der Fake-Funktion, die in Relation zu den später zu messenden Funktionen vernachlässigbar kurz ist. Die bisherigen Läufe ergaben alle eine vernachlässigbare Laufzeit der Timing-Schleife. Gegebenenfalls kann man den hier ermittelten Wert dann auch von den späteren Messergebnissen abziehen. Bei den 10000 Aufrufen liegt der Fehler deutlich unter 1/1000.

Übrigens: Wenn deine Delphi-Version noch keine TStopwatch kennt, es gibt eine offizielle und frei verfügbare Vorgängerversion von Allen Bauer: News Flash! Someone has already invented the wheel!

Harry Stahl 15. Jul 2018 11:31

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407303)
Anhang 49520So in etwa würde neue GUI aussehen.
Wem noch mehr sinnvolle Optionen einfallen, bitte melden.

Edit
Ps: Ein Cancel Button ist nun vorhanden :)

Wenn man die zu startenden Funktionen in einer Checklist-Box zuvor auswählen könnte, fände ich gut (evtl. über Popup-Menü mit Einträgen für alle abwählen und alle anwählen). Denn wenn man an einer Funktion evtl. selber noch was rumschrauben wollte, müsste man nicht alle Tests durchlaufen lassen...

Ansonsten::thumb:

KodeZwerg 15. Jul 2018 12:54

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Harry Stahl (Beitrag 1407317)
Denn wenn man an einer Funktion evtl. selber noch was rumschrauben wollte, müsste man...

...in der ComboBox "Run All Tests" sich auf den einen veränderten Festlegen oder selbst alles umschreiben.

Sorry, nach Uwe's Text kann ich gerade nicht daran weiterwerkeln, das wäre vergebene müh. Ich mag ja gerne Kritik annehmen aber umsetzen ohne Zusatz-Komponenten.
Wie es jetzt gerade ist dachte ich eigentlich ist es okay für synthetische und praktische Wertungen.
Da habe ich mir wohl was vorgemacht.

Seis drum, Source ist vorhanden, macht damit was ihr wollt.

Ich würde zu guter Letzt gerne noch Erfahren ob denn meine Berechnung der Avg-Variable korrekt ist oder auch gänzlich falsch?

jbg 15. Jul 2018 13:10

AW: Anzahl eines Zeichens im String ermitteln
 
So und jetzt eine Variante mit SIMD (SSE2, benötigt mindestens Nehalem Prozessorarchitektur von 2008). Sie ist mit Delphi 2009 kompilierbar und dürfte alle bisherigen Funktion "wegblasen".

Zitat:

00000 Calibrate
03279 1234588 miep
05454 Ydobon
02642 marabu
03377 Missionar
03252 alzaimar
02661 Uwe Raabe StringCountChar
02635 Uwe Raabe StringCountCharFor
02346 KodeZwerg CountCharInString
06970 KodeZwerg CharInStringA
03529 Neutral General CharCountAsm
01779 Andreas Hausladen CountCharAsm
00515 Andreas Hausladen CountCharAsmSIMD
01953 Uwe Raabe CharCount
02713 Egon Hugeist CharCount_1
03261 Egon Hugeist CharCount_2
02640 Egon Hugeist CharCount_Double_Sided_3
02695 Egon Hugeist CharCount_Double_Sided_4
03328 Delphi CountChar

Delphi-Quellcode:
function AH_CountCharAsmSIMD(const Str: String; c: Char): Cardinal;
asm
{$IFNDEF CPUX64}
  test eax, eax
  jz @@Empty      // wenn S = '' then gibt es nichts zu tun

  push ebx
  push esi
  mov esi, eax
  xor eax, eax // Rückgabewert auf 0 setzen

  // Stringlänge ermitteln
  //mov ecx, DWORD PTR [esi-skew].StrRec.Length
  mov ecx, DWORD PTR [esi-$04]
  shl ecx, 1 // Auf Byte-Offsets umrechnen
  lea esi, [esi+ecx] // ESI so umrechnen, dass ESI+ECX auf das String-Ende zeigen
  neg ecx
  cmp ecx, -8
  ja @@SingleChars

  // DX in XMM0 als DX:DX:DX:DX:DX:DX:DX:DX verteilen
  movd xmm0, edx
  pshuflw xmm0, xmm0, 0
  pshufd xmm0, xmm0, 0

  // Offset so ändern, damit nicht über das String-Ende hinauslesen gelesen wird
  add ecx, 16
  jc @@RemainingChars
  sub esi, 16

  nop // Alignment
  // 8 Zeichen auf einmal verarbeiten
@@Loop8WideChars:
  movdqu xmm1, [esi+ecx] // 16 Bytes (8 Zeichen) laden
  pcmpeqw xmm1, xmm0 // Vergleichen. in XMM1 steht danach für jedes gefundene Zeichen $FFFF an der WORD Position
  pmovmskb ebx, xmm1 // Das oberste Bit jedes Bytes in XMM1 in EBX als Bit-Maske übertragen
  popcnt ebx, ebx // Anzahl der Bits ermitteln (CPU muss POPCNT unterstützen, Nehalem von 2008)
  shr ebx, 1 // Da PMOVMSKB auf Bytes anstatt auf WORDs arbeitet, haben wir doppelt so viele Bits => div 2
  add eax, ebx

  add ecx, 16
  jz @@Leave
  jnc @@Loop8WideChars

  // Offset für den Einzelzeichen-Vergleich wiederherstellen
  add esi, 16
@@RemainingChars:
  sub ecx, 16
  cmp ecx, -8
  ja @@SingleChars

  // 4 Zeichen auf einmal verarbeiten
  movq xmm1, QWORD PTR [esi+ecx] // 8 Bytes (4 Zeichen) laden
  pcmpeqw xmm1, xmm0 // Vergleichen. In XMM1 steht danach für jedes gefundene Zeichen $FFFF an der WORD Position
  pmovmskb ebx, xmm1 // Das oberste Bit jedes Bytes in XMM1 in EBX als Bit-Maske übertragen
  movzx ebx, bl // Nur die unteren 8 Bits sind korrekt befüllt alle anderen auf 0 setzen
  popcnt bx, bx // Anzahl der Bits ermitteln (CPU muss POPCNT unterstützen, Nehalem von 2008)
  shr ebx, 1 // Da PMOVMSKB auf Bytes anstatt auf WORDs arbeitet, haben wir doppelt so viele Bits => div 2
  add eax, ebx

  add ecx, 8
  jz @@Leave

  nop
@@SingleChars:
  xor ebx, ebx
@@Loop:
  cmp WORD PTR [esi+ecx], dx
  sete bl
  add eax, ebx
  add ecx, 2
  jnz @@Loop

@@Leave:
  pop esi
  pop ebx
@@Empty:
{$ELSE}
  xor rax, rax // Rückgabewert auf 0 setzen

  test rcx, rcx
  jz @@Leave      // wenn S = '' then gibt es nichts zu tun

  mov r8, rcx

  // Stringlänge ermitteln
  //movsxd r9, DWORD PTR [r8-skew].StrRec.Length
  movsxd r9, DWORD PTR [r8-$04]
  shl r9, 1  // Auf Byte-Offsets umrechnen
  lea r8, [r8+r9] // E8 so umrechnen, dass R8+R9 auf das String-Ende zeigen
  neg r9
  cmp r9, -8
  ja @@SingleChars

  // DX in XMM0 als DX:DX:DX:DX:DX:DX:DX:DX verteilen
  movd xmm0, edx
  pshuflw xmm0, xmm0, 0
  pshufd xmm0, xmm0, 0

  // Offset so ändern, damit nicht über das String-Ende hinauslesen gelesen wird
  add r9, 16
  jc @@RemainingChars
  sub r8, 16

  nop; nop // alignment
  // 8 Zeichen auf einmal verarbeiten
@@Loop8WideChars:
  movdqu xmm1, [r8+r9] // 16 Bytes (8 Zeichen) laden
  pcmpeqw xmm1, xmm0 // Vergleichen. In XMM1 steht danach für jedes gefundene Zeichen $FFFF an der WORD Position
  pmovmskb ecx, xmm1 // Das oberste Bit jedes Bytes in XMM1 in ECX als Bit-Maske übertragen
  popcnt ecx, ecx // Anzahl der Bits ermitteln (CPU muss POPCNT unterstützen, Nehalem von 2008)
  shr ecx, 1 // Da PMOVMSKB auf Bytes anstatt auf WORDs arbeitet, haben wir doppelt so viele Bits => div 2
  add eax, ecx

  add r9, 16
  jz @@Leave
  jnc @@Loop8WideChars

  // Offset für den Einzelzeichen-Vergleich wiederherstellen
  add r8, 16
@@RemainingChars:
  sub r9, 16
  cmp r9, -8
  ja @@SingleChars

  // 4 Zeichen auf einmal verarbeiten
  movq xmm1, QWORD PTR [r8+r9] // 8 Bytes (4 Zeichen) laden
  pcmpeqw xmm1, xmm0 // Vergleichen. in XMM1 steht danach für jedes gefundene Zeichen $FFFF an der WORD Position
  pmovmskb ecx, xmm1 // Das oberste Bit jedes Bytes in XMM1 in ECX als Bit-Maske übertragen
  movzx ecx, cl // Nur die unteren 8 Bits sind korrekt befüllt alle anderen auf 0 setzen
  popcnt cx, cx // Anzahl der Bits ermitteln (CPU muss POPCNT unterstützen, Nehalem von 2008)
  shr ecx, 1 // Da PMOVMSKB auf Bytes anstatt auf WORDs arbeitet, haben wir doppelt so viele Bits => div 2
  add eax, ecx

  add r9, 8
  jz @@Leave

  nop // Alignment
@@SingleChars:
  xor ecx, ecx
@@Loop:
  cmp WORD PTR [r8+r9], dx
  sete cl
  add eax, ecx
  add r9, 2
  jnz @@Loop
@@Leave:
{$ENDIF ~CPUX64}
end;

KodeZwerg 15. Jul 2018 13:39

AW: Anzahl eines Zeichens im String ermitteln
 
Danke Andreas, mit Deinem "{$IFNDEF CPUX64}" fix funktionieren nun alle ASM Varianten, auch die von jaenicke, bei mir. Krasse Ergebnisse, mein lieber Herr Gesangsverein.

Uwe Raabe 15. Jul 2018 14:04

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407322)
Ich würde zu guter Letzt gerne noch Erfahren ob denn meine Berechnung der Avg-Variable korrekt ist oder auch gänzlich falsch?

So ganz verstehe ich noch nicht, was du mit deine avg-Berechnung genau erreichen willst. Die generelle Vorgehensweise für eine Mittelwertberechnung ergibt sich aus der Definition:

avgn := Sumi:1..n(xi)/N; // xi = Wert bei Iteration i, N Anzahl Iterationen.

Rekursiv ergibt sich daraus:
avgn+1 := (avgn*N + xn+1)/(N+1)

Delphi-Quellcode:
      if Loops = 0 then
      begin
        Last := Curr;
        Min := Curr;
        Max := Curr;
        Avg := Curr;
      end
      else
      begin
        if Curr < Min then Min := Curr;
        if Curr > Max then Max := Curr;
        Avg := (Loops*Avg + Curr)/(Loops + 1);
        Last := Curr;
      end;
Einfacher wäre aber die Berechnung der Summe innerhalb der Schleife und anschließende Division durch die Anzahl für das avg nach der Schleife.

Uwe Raabe 15. Jul 2018 14:15

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von jbg (Beitrag 1407323)
So und jetzt eine Variante mit SIMD (SSE2, benötigt mindestens Nehalem Prozessorarchitektur von 2008).

Ich erweitere dann mal den Benchmark auf OSX, Linux, Android und iOS :twisted:

jbg 15. Jul 2018 14:39

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1407332)
Ich erweitere dann mal den Benchmark auf OSX, Linux, Android und iOS :twisted:

Für diese Betriebssysteme bzw. eher Platformen nutzt man auch nicht unbedingt Delphi als erste Wahl. Da spuckt einem der entsprechende Compiler schon den tausend mal besseren Code aus.

KodeZwerg 15. Jul 2018 15:17

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1407331)
So ganz verstehe ich noch nicht, was du mit deine avg-Berechnung genau erreichen willst.

Ich wollte damit den tatsächlichen Durchschnittswert damit ermitteln und nicht die Mitte von Min und Max als Score festlegen.
Dank Deines Links und Text bin ich nun im Bilde.

edit
Delphi-Quellcode:
      if Loops = 0 then
      begin
        Min := Curr;
        Max := Curr;
        Avg := Curr;
      end
      else
      begin
        if Curr < Min then Min := Curr;
        if Curr > Max then Max := Curr;
        Avg := Avg + Curr;
      end;
      if fCancel = True then Break;
    end; // <- hier endet eine For-Schleife (Loops)
    Avg := Avg / Loops;
So habe ich es nun geregelt, sollte overhead reduzieren. Vielen Dank Uwe!

EgonHugeist 15. Jul 2018 17:10

AW: Anzahl eines Zeichens im String ermitteln
 
Kleine Anregungen:

Zitat:

Funktionen von KodeZwerg
CountCharInString, CharInStringA scheitern, wenn #0 im String sind und knallen wenn ein #0 gesucht wird. letzteres sollte mindistens abgefangen und ersteres dokumentiert werden

Zitat:

Funktion AH_CharCountAsm von Andreas aka AHUser aka jbg
ich meine das Result wird nicht initialisiert, wenn der String lehr ist

Zitat:

Aufruf Funktion CharCountAssembler
für CharCountAsm von Neutral General knallte mit {$R+), wenn der String lehr ist. Die function CharCountAsm selber zählt jedoch immer +1 wenn AStr = nil

Zitat:

function IFCount von alzaimar und miep
ist nicht NEXTGEN safe -> falscher Index vom String

Des weiteren muß ich KodeZwerg, wegen der Bemerkung über die Lösung von Ydobon recht geben, zu viele theoretische MemAllocs, welche nicht vergleichbar wären, wenn ... wird den der Code wirklich zu 100%, wie geschrieben, unter XE10.2 ausgeführt??? Weiß der Teufel, wie das zu geht.

im Test selber sollte der gesuchte Char, sowohl die Größe als auch Inhalt des Strings wechseln um alle Ungereimtheiten auszuschließen.

Ansonsten folgende Aufgabe in den Raum gestellt:

Ich bekomme die Aufgabe die Anzahl eines Buchstaben in einem Buch zu finden. Ich soll sie von 1 bis 10000 wiederholen. Buchstabe und Buch wechseln nicht (lt. Uwes Test). Angenommen ihr währed unfehlbar und das Ergebnis steht nach Durchlauf 1 fest ... Wie oft würdet ihr den Job tatsächlich machen?

Die damit verbundene Hypothese: Könnte der Compiler soweit optimiert wurden sein, solch ein Scenario zu erkennen?

Mir kommt Uwes Test a bizzl so vor, als messen wir nur den einmaligen Aufruf-Stack..

Edit:

@@@@jbg

Einfach der Hammer !!! Aber schau mal ob dein Result initialisiert ist, wenn nix zu tun ist!

Uwe Raabe 15. Jul 2018 17:26

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von EgonHugeist (Beitrag 1407343)
Mir kommt Uwes Test a bizzl so vor, als messen wir nur den einmaligen Aufruf-Stack..

Ich hatte bereits probeweise mal die Anzahl Durchläufe auf 100.000 erhöht und auf 1000 erniedrigt. Dabei sind die Zeiten im Bereich der bekannten Schwankungen zwischen verschiedenen Programmstarts ziemlich konsistent um den Faktor 10 verändert worden. Um einen einzelnen Aufruf zu messen reicht die Auflösung meiner Meinung nach einfach nicht aus.
Alles Win32 (die Zeiten für 100000 dauern mir jetzt zu lange):
Zitat:

Zitat von 10000 calls
00000 Calibrate
03575 1234588 miep
05879 Ydobon
02967 marabu
03797 Missionar
03665 alzaimar
02977 Uwe Raabe StringCountChar
03051 Uwe Raabe StringCountCharFor
02709 KodeZwerg CountCharInString
07248 KodeZwerg CharInStringA
04035 Neutral General CharCountAsm
02026 Andreas Hauladen CharCountAsm
02785 Uwe Raabe CharCount
03052 Egon Hugeist CharCount_1
03648 Egon Hugeist CharCount_2
02899 Egon Hugeist CharCount_3
03226 Egon Hugeist CharCount_4
02464 Egon Hugeist CharCount_5
03382 Egon Hugeist CharCount_6
03693 Delphi CountChar

Zitat:

Zitat von 1000 calls
00000 Calibrate
00353 1234588 miep
00585 Ydobon
00299 marabu
00373 Missionar
00361 alzaimar
00296 Uwe Raabe StringCountChar
00293 Uwe Raabe StringCountCharFor
00261 KodeZwerg CountCharInString
00681 KodeZwerg CharInStringA
00385 Neutral General CharCountAsm
00199 Andreas Hauladen CharCountAsm
00269 Uwe Raabe CharCount
00300 Egon Hugeist CharCount_1
00357 Egon Hugeist CharCount_2
00283 Egon Hugeist CharCount_3
00299 Egon Hugeist CharCount_4
00229 Egon Hugeist CharCount_5
00334 Egon Hugeist CharCount_6
00366 Delphi CountChar

Es ist auch so, daß die Kalibrierung mit der Fake-Funktion den eigentlichen Funktionsaufruf auch mit berücksichtigt (allein schon, um eine Wegoptimierung der Schleife zu verhindern). Ist aber trotzdem vernachlässigbar.

EgonHugeist 15. Jul 2018 17:39

AW: Anzahl eines Zeichens im String ermitteln
 
@Uwe

ist nur 'ne bescheidene Hypothese. Dennoch sind mir die Ergebnisse von Ydobon viel zu nah an allen anderen. Bist du da mit mir einig? IMO spukt es da ein wenig. Alle sollten sich je nach {$R+} minimal aneinander reihen. Die Theorie die Optimirung von StringReplace ist für mich vom Tisch...

Uwe Raabe 15. Jul 2018 17:54

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von EgonHugeist (Beitrag 1407346)
Dennoch sind mir die Ergebnisse von Ydobon viel zu nah an allen anderen. Bist du da mit mir einig? IMO spukt es da ein wenig. Alle sollten sich je nach {$R+} minimal aneinander reihen. Die Theorie die Optimirung von StringReplace ist für mich vom Tisch...

Solltest du nochmal überdenken. Ich habe den Code mal unter D2009 compiliert und auf meinem System ausführen lassen, diesmal allerdings nur für 10(!!!) Durchäufe (aus gleich ersichtlichen Gründen):
Zitat:

00000 Calibrate
00005 1234588 miep
26715 Ydobon
00005 marabu
00006 Missionar
00005 alzaimar
00008 Uwe Raabe StringCountChar
00005 Uwe Raabe StringCountCharFor
00004 KodeZwerg CountCharInString
00008 KodeZwerg CharInStringA
00004 Neutral General CharCountAsm
00002 Andreas Hauladen CharCountAsm
00002 Uwe Raabe CharCount
00003 Egon Hugeist CharCount_1
00003 Egon Hugeist CharCount_2
00003 Egon Hugeist CharCount_3
00003 Egon Hugeist CharCount_4
00002 Egon Hugeist CharCount_5
00003 Egon Hugeist CharCount_6

jbg 15. Jul 2018 18:21

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von EgonHugeist (Beitrag 1407343)
Zitat:

Funktion AH_CharCountAsm von Andreas aka AHUser aka jbg
ich meine das Result wird nicht initialisiert, wenn der String lehr ist

Wenn EAX schon 0 ist, muss man es nicht nochmal auf 0 setzen. In der 64-Bit Version hast du aber recht. Da wird der erste Parameter in RCX anstatt in RAX übergeben (Copy&Paste von x86 halt).

Ich habe die 64-Bit Version in meinen beiden Posts korrigiert.

KodeZwerg 15. Jul 2018 18:25

AW: Anzahl eines Zeichens im String ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
Falls jemand mag, im Anhang ist eine Version wo alles adjustable ist, so kann jeder selbst entscheiden wie vorgegangen werden soll.
Es kann nun in jedem Durchlauf neue Data generiert werden. <- Verlängert Tests, mehr overhead!
Es kann nun in jedem Durchlauf nach was anderem gesucht werden (ich nahm Char(Random($FF))).

Ja ich werde die KodeZwerg Methoden updaten, jetzt war funktionalität im Vordergrund.

KodeZwerg 15. Jul 2018 18:32

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1407347)
Ich habe den Code mal unter D2009 compiliert und auf meinem System ausführen lassen, diesmal allerdings nur für 10(!!!) Durchäufe (aus gleich ersichtlichen Gründen):

Genau deswegen war ich immer so Überrascht wo bei mir denn der Fehler sei. Das war auch der Grund für eine Extra-CheckBox dafür.

EgonHugeist 15. Jul 2018 18:33

AW: Anzahl eines Zeichens im String ermitteln
 
@Uwe
Gerne, was früher war muß längst nicht mehr sein... Bin da immer offen!
Um etwas zu lernen, hilft es mir es zu verstehen. Schnell im Pure-Pascal ist etwas, was ich mag.

Daher meine Hypothese, welche meine alleinige Annahme ist und ich auch nicht beweisen kann..

@KodeZwerg
Ich kuller mal ein Auge drüber, danke! Sei so gut und check meine Anmerkungen, bitte.

@jbg
Super!

Uwe Raabe 15. Jul 2018 20:46

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von EgonHugeist (Beitrag 1407353)
Um etwas zu lernen, hilft es mir es zu verstehen. Schnell im Pure-Pascal ist etwas, was ich mag.

Wenn ich mir die Implementation von StringReplace in D2009 und Tokyo ansehe, dann sind die doch schon sehr verschieden. Die Implementation in D2009 (34 Zeilen) macht einen Haufen Copy und Result := Result + NewStr, während Tokyo (212 Zeilen) zunächst die Anzahl der Fundstellen ermittelt und damit die Länge von result ermittelt und bereitstellt. Danach wird einfach nur mehrfach ein Move in Result gemacht. Das ist unterm Strich offenbar deutlich schneller. Der Code in 2009 ist dafür leichter zu verstehen und straight forward. In der Tokyo Version möchte ich keinen Fehler suchen.

EgonHugeist 16. Jul 2018 05:20

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1407356)
Zitat:

Zitat von EgonHugeist (Beitrag 1407353)
Um etwas zu lernen, hilft es mir es zu verstehen. Schnell im Pure-Pascal ist etwas, was ich mag.

Wenn ich mir die Implementation von StringReplace in D2009 und Tokyo ansehe, dann sind die doch schon sehr verschieden. Die Implementation in D2009 (34 Zeilen) macht einen Haufen Copy und Result := Result + NewStr, während Tokyo (212 Zeilen) zunächst die Anzahl der Fundstellen ermittelt und damit die Länge von result ermittelt und bereitstellt. Danach wird einfach nur mehrfach ein Move in Result gemacht. Das ist unterm Strich offenbar deutlich schneller. Der Code in 2009 ist dafür leichter zu verstehen und straight forward. In der Tokyo Version möchte ich keinen Fehler suchen.

So was habe ich mir schon gedacht.. :thumb: für das Abändern der function! Ich kenne sehr viele implementationen von StringReplace, alle jedoch reservieren Speicher für das Resultat. Hier kommt noch eine Sache zum Tragen: der Char ist kein String, somit sollte abermals Speicher (WideChar->UnicodeString) reserviert werden, wenn da nicht inzwischen ein overload existiert. Gehen wir davon aus, die neue Variante wurstelt erst mal die Anzahl der vorgekommenen SubStr's raus, hat einen statischen Pool von Markern vorreserviert, und braucht nur noch in das Result schreiben(wenn Result string refcount 1 ist). SysFreeMem und SysGetMem setzen/flushen nur (dank Idee von Pierre le Richie -> verzögertes freigeben ) noch das Flag für den Speicherbereich(oder hält der Compiler das Result auf alle Ewigkeit im Speicher?). Kling alles sehr gut!

Dennoch stehen die Calls(50Cycles/Call) auf StringReplace, ?WChar2UStr?, GetMem, FreeMem und 240 Zeilen StringReplace im Raum (Length ist seit D2005 inlined und macht das gleiche, wie mein NativeCode oder es in den ASM varianten zu sehen ist) und 2x Code für die Stringlänge, wogegen jede andere function ohne all dem auskommt. Ich meine man sollte da schon einen merklichen Nachteil erkennen können. Darum die Hypothese. Und die An,erkung über den Test Selber, das der String in seiner Größe und Inhalt permanent wechseln sollte. Wäre dem so(IMHO usual case), würde sich die StringReplace Variante auch auf dem neuen Compiler deutlich mehr abheben und die Nachteile erleutern (der Lerneffekt für andere Benutzer sollte ja gegeben sein, oder?).

Ich bin dennoch sehr beeindruckt, wie weit der Compiler Fortschritte für Optimierungen all der Schritte inzwischen gemacht hat. Würde mir ein Chart wünschen, welches solche Effeckte je nach Compiler-Entwicklungsforschritt(alt vs. neu) mal darstellt. Gibt es ein solches? Es gäbe mit Sicherheit eine Ziehlgruppe, die solche Darstellungen eher zum Kauf/Update bewegen würden als FMX,Generics,NEXTGEN &Co.

Zitat:

Zitat von Harry Stahl (Beitrag 1407269)
Zitat:

Zitat von EgonHugeist (Beitrag 1407268)
Aber danke auch an Dich für den Hinweis mit der blockweisen Bearbeitung, da kommen mir Ideen für andere Funktionen (z.B. in der Bitmap-Bearbeitung)...


Gern geschehen. Auch die 2. ASM Variante von Andreas zeigt die Technik auf (viel genialer umgesetzt dank seiner ASM Kenntnisse). Der Effeck einer Blockweisen Abarbeitung innerhalb einer Big-Loop wird deutlich größer, desto mehr insgesammt verarbeitet wird. Das Thema hier ist da von der Aufgabenstellung her zu maginal für meine Pure-Pascal Version um den Effekt aufzuzeigen zu können. Es entsteht jedoch deutlich mehr Code der gepflegt werden muß, also macht die Technik auch nicht überall Sinn, könnte sich sogar negativ auswirken. Hinzu kommt das viele den Sinn nicht erkennen können. Da gibt es Leute, die fangen mit 'nem Thread-Pool/TParallel an, wo diese Technink schon ausreichend oder effizienter wäre.

KodeZwerg 16. Jul 2018 09:17

AW: Anzahl eines Zeichens im String ermitteln
 
Also das mit dem Replace und wie der Delphi 2009 Kompiler damit umgeht versteh ich nicht.
Zitat:

0000303114 - Statistic: [Binary Data] [303114 Avg] [302392 Min] [303835 Max] [606227 Full] @ KodeZwerg TestStringBuilder()
0000620229 - Statistic: [ASCII Data] [620229 Avg] [4518 Min] [1235939 Max] [1240458 Full] @ KodeZwerg TestStringBuilder()
0002884451 - Statistic: [Binary Data] [2884451 Avg] [2853455 Min] [2915447 Max] [5768902 Full] @ Ydobon
0005993589 - Statistic: [ASCII Data] [5993589 Avg] [4226 Min] [11982953 Max] [11987178 Full] @ Ydobon
Delphi-Quellcode:
// Ydobon =    i := Length(Data) - Length(StringReplace(Data, Ch, '', [rfReplaceAll]));

function TCharInString.TestStringBuilder(const aString: string; const Ch: Char): Integer;
var
  sb: TStringBuilder;
begin
  sb := TStringBuilder.Create;
  sb.Append(aString);
  sb.Replace(Ch, '');
  Result := Length(aString) - sb.Capacity;
  FreeAndNil(sb);
end;
Ich mache doch theoretisch genau das selbe wie Ydobon aber ein enormer Geschwindigkeitszuwachs. (Nach wie vor auf letzten Platz)
Eine neue Version mit noch mehr nützlichen Optionen ist bereits fertig, auch Egons fehlende Methoden sind enthalten.

Wenn ich meine StrScan() Methode korrigiere, kommt Uwes Variante bei raus also habe ich nun eine Sicherheits-CheckBox die CountCharInString(), CharInStringA() und CharCount() vom Test ausschließt.
CharInStringA() weist noch einen bösen Fehler auf, Char <> AnsiChar Konvertierung schlägt sporadisch fehl, ich vermute ab Char 7F wird es knallen.

So wie Uwe es gemacht hat, mit einer Werte-Kontrolle kann ich bei mir so nicht einbauen da doppelter overhead produziert werden würde (ist dem Random(Char) zu verdanken ^_^)

Uwe Raabe 16. Jul 2018 09:43

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1407372)
Also das mit dem Replace und wie der Delphi 2009 Kompiler damit umgeht versteh ich nicht.

Das StringReplace in D2009 verwendet ja auch keinen StringBuilder und das dortige Replace, sondern in etwa folgenden Algorithmus:
Code:
1. Initialisiere Result mit einem Leerstring und setze die Position auf den Anfang des Strings
2. Suche das nächste Vorkommen des Such-Patterns
3. Hänge den String vom Start bis zur Startposition des Such-Patterns bzw. Ende des Strings (wenn nicht gefunden) an den Result an
4. Brich ab, wenn das Such-Pattern nicht gefunden wurde
5. Hänge das Ersetzen-Pattern dran
6. Rücke vor bis zum Ende des Such-Patterns
7. Wiederhole ab Schritt 2
Der Result-String wird also bei jedem gefundenen Zeichen zweimal verlängert, wobei in vielen Fällen wohl ein komplettes Umkopieren von Result nötig ist. Das StringReplace ist in D2009 eben extrem unperformant und speicherhungrig implementiert, dafür aber deutlich leichter zu lesen und zu verstehen.

Es ist noch anzumerken, daß diese StringReplace-Optimierung erst in Delphi 10.1 Berlin eingeführt wurde.

KodeZwerg 16. Jul 2018 09:57

AW: Anzahl eines Zeichens im String ermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1407381)
Es ist noch anzumerken, daß diese StringReplace-Optimierung erst in Delphi 10.1 Berlin eingeführt wurde.

Ja, so etwas habe ich mir beim Ausführen der von Euch hochgeladenen .exes gedacht.
Wenn Du heute mal Zeit hast würde mich ein Ergebnis Tokyo Ydobon vs TestStringBuilder() Interessieren, ob da der StringBuilder auch noch die Nase vorne hat?

KodeZwerg 16. Jul 2018 20:47

AW: Anzahl eines Zeichens im String ermitteln
 
Hab noch etwas gebastelt aber ist auch nicht sehr effektiv.
Delphi-Quellcode:
// versuche 4 compares pro loop abzufertigen
function TCharInString.MultiCountChar(const s: string; const c: Char): Integer;
var
  i, ii, iii, iiii: Integer;
begin
  Result := 0;
  ii := Length(s);
  iii := (ii div 2);
  iiii := iii+1;
  for i := 1 to (Length(s) div 2) do
  begin
    if s[i] = c then Inc(Result, 1); // suche vom Anfang in Richtung Mitte
    if s[ii] = c then Inc(Result, 1); // suche vom Ende in Richtung Mitte
    if s[iii] = c then Inc(Result, 1); // suche von Mitte in Richtung Anfang
    if s[iiii] = c then Inc(Result, 1); // suche von Mitte in Richtung Ende
    Dec(ii);
    Dec(iii);
    Inc(iiii);
    if i = iii then Break;
  end;
end;

himitsu 17. Jul 2018 09:40

AW: Anzahl eines Zeichens im String ermitteln
 
Sinnlos auf unnötig viele Vaiablen zugreifen und im Speicher kreuzundquer? Da kann dann nichtmal ein ordentliches Pageging/Caching gut arbeiten.
Eventuell werden Chars doppelt, dreifach oder gar vierfach gezählt und anstatt die Schleife gleich im richtigen Bereich laufen zu lassen noch eine zusätzliche Abbruchprüfung.

Eine Schleife mit nur einer Zähler- und Ende-Variable und zugriff auf nur eine Page nach der Anderen, anstatt kreuzundquer.

Oder, 4 oder 8 zusammenhängende und ausgerichtete (align) Bytes in ein Register (lokale temporäre Variable innerhalb der Schleife) einlesen und direkt im Register prüfen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:16 Uhr.
Seite 4 von 4   « Erste     234   

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz