AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Leerzeichen in String einfügen

Ein Thema von majornudelholz · begonnen am 26. Jan 2016 · letzter Beitrag vom 2. Feb 2016
Antwort Antwort
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.155 Beiträge
 
Delphi 10.3 Rio
 
#1

AW: Leerzeichen in String einfügen

  Alt 28. Jan 2016, 09:16
Leute, im Ernst: Ist hier nicht ausnahmsweise angebracht (ja, ich weiß, es gibt Programmierer, die lehnen das ab), die einfachste Variante vorzuschlagen?
emmmmm nöö

Wir haben ja oft hier Neulinge die mit Delphi anfangen. Nach der einfachen Regel:

Make it Work Make it Right Make It Fast!

Ich gebe Dir recht, dass für den TE die Insert Variante ausreichend ist. Aber auch er sollte im Hinterkopf behalten, dass es zwar funktioniert, aber nicht gerade performant ist.

Daher finde ich meine Variante als gutes Beispiel. (Die RTL macht es auch so) Für so einen Fall "C-Like" mit PChars zu arbeiten. Dabei bleibt die Routine immer noch übersichtlich. Und ist immerhin 60x schneller... Wir regen hier nicht von 2x oder 3x!

Sir Rufo's Variaten hat aber auch Ihren Platz - wenn auch nicht so schnell - kann man diese Routine, so übernehmen und als 1. Routine in seine eigene Procedurensammlung packen - weil allgemein gültig... Dabei immerhin auch 6,7x schneller als die Insert Variante. Not Bad für eine allgemeingültige Routine...

Hugo hat dann gezeigt... Richtig Kopieren bringt nochmal eine mehr als doppelt so schnelle Version hervor. Auch wenn die Routine dann nicht mehr ganz so übersichtlich ist.

Eigentlich wollte ich noch eine Thread-Variante bauen, die den String erst auf 4 Worker-Thread aufteilt, aber ich hatte leider gestern keine Zeit mehr... Vielleicht macht es ja noch einer...

Im Großen und Ganzen... Finde ich den Thread sehr gelungen, auch wenn das Thema jetzt damit abgeschlossen ist. Wenn jeder Programmierer - selbst bei so wenigen Zeilen Source - die Performance Überlegungen nicht außer acht lassen würde, brächten wir heute nicht 3,5 GHz, um Dinge zu erledigen, die früher mit 25 MHz möglich waren... (bitte nicht darauf eingehen)

Mavarik
  Mit Zitat antworten Zitat
EgonHugeist

Registriert seit: 17. Sep 2011
187 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: Leerzeichen in String einfügen

  Alt 28. Jan 2016, 10:54
@Mavarik

so da gibt jetzt der "Hugo" mal noch abschließend seinen Senf hinzu.

Dem Link stimme ich vollends zu.

Bitte schau es dir mal genauer an, was ich mache. Verstanden hast du es auch noch nicht. Der Trick ist nicht das kopieren. Das könnte man in den Quad folgen auch so lösen:
Code:
.....
Dest^ := Src^;
(Dest+1)^ := (Src+1)^;
(Dest+2)^ := (Src+3)^;
(Dest+3)^ := (Src+3)^;
.....
Und es würde nachwievor jede hier dargestellte Version schlagen.

Der Trick ist das Decrementieren des PEnd-Pointers um die Länge des concat-Strings. Somit kann ich in jedem Falle den gesamten concat innerhalb Lenght(Result) div Length(ConcatStr) Loop durchlaufen und ich brauch auch nicht mehr auf die Position zu achten, damit die N-te ein String-X wird.

Somit läuft mein code mit 4x weniger loops als der deine.

Die Loops waren das Performance Problem bei dir und bei Sir Rufo gleich doppelt + der Bremse, daß die IDE innerhalb jeder Loop die Offsets neu incrementieren muß, da er Str[x] offsets nutzt.

Sir Rufos Bsp könnte man auch so darstellen (nicht hauen wenns jettzt nicht geht -> ungetestet):
Code:
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var
  AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer;
  PAStr, Dest, PEnd: PChar;
begin
  if AStr = '' then
    Result := ''
  else begin
    AStrLen := PLongInt(NativeUInt(AStr) - SizeOf(LongInt))^; //fast get AStr length
    if (AInsertStr = '') or (AStrLen <= APos) then
      Result := AStr
    else begin
      AInsertStrLen := PLongInt(NativeUInt(AInsertStr) - SizeOf(LongInt))^; //fast get AInsertStr length
      ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen);
      ConcatLen := APos+AInsertStrLen;
      SetLength(Result, ResLen);
      PAStr := Pointer(AStr);
      Dest := Pointer(Result);
      PEnd := (PAStr+AStrLen)-APos; <<<<<<<<<<<<<<----- Da hat der Frosch die Locken (:
      while (PAStr < PEnd) do begin
        System.Move(PAStr^, Dest^, APos{$IFDEF UNICODE} shl 1{$ENDIF});
        System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen{$IFDEF UNICODE}shl 1{$ENDIF});
        Inc(Dest, ConcatLen);
        Inc(PAStr,APos);
      end;
      System.Move(PAStr^, Dest^, ((PEnd+APos) - PAStr){$IFDEF UNICODE} shl 1{$ENDIF});
    end;
  end;
end;
und nun noch mal für den Laien verständlicher mit Anleitung und ohne Low-Level code:
Delphi-Quellcode:
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var
  AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer;
  PAStr, Dest, PEnd: PChar;
begin
  if AStr = 'then
    Result := '
  else begin
    { speichere die Länge das Strings, in welchen die Inserts gemacht werden sollen }
    AStrLen := Length(AStr);
    if (AInsertStr = '') or (AStrLen <= APos) then
      Result := AStr
    else begin
      { Vorbereitung }
      AInsertStrLen := Length(AInsertStr); //speichere die Länge des AInsertStrings
      ConcatLen := APos+AInsertStrLen; //speichere die Länge der Fragmente welche im Resultat zusammengesetzt werden

      ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen); //kalkuliere die Länge des Resultats
      SetLength(Result, ResLen); //reserviere den benötigten Speicher für das Resultat

      PAStr := Pointer(AStr); //Speichere den Pointer von AStr
      Dest := Pointer(Result); //Speichere den Pointer von Result
      
      { Speichere den Pointer des letzten Chars von AStr abzüglich die Länge von APos }
      { um die Wiederholungen der nachfolgenden Schleife um Faktor APos zu reduzieren } 
      PEnd := (PAStr+AStrLen)-APos;

      { fülle das Resultat mit den Daten }
      while (PAStr < PEnd) do begin
        { da wir PEnd um die Länge von APos reduziert haben, ist es garantiert, das wir NIE die Länge von AStr überschreiten }
        
        { kopiere eine Menge von APos Chars von der derzeitig gemerkten Stelle des AStr in das Resultat }
        System.Move(PAStr^, Dest^, APos*SizeOf(Char));
        { kopiere AInsertString in das Resultat nach den zuletzt kopierten Daten }
        System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen *SizeOf(Char));

        { passe die OffSets an }
        Inc(Dest, ConcatLen); //Erhöhe den Pointer des Resultats um die Menge der kopierten Chars
        Inc(PAStr, APos); //Erhöhe den Pointer des AStr um APos Stellen
      end;
      Inc(PEnd+APos); //nun inkrementiere PEnd wieder um APos Stellen, welche wir oben abgezogen haben
      { kopiere den Rest oder garnix (welches sich aus der Differenz von PEnd - PAStr ergibt) von AStr ins Resultat }
      System.Move(PAStr^, Dest^, (PEnd - PAStr)* SizeOf(Char));
    end;
  end;
end;
Ich habs jetzt mal Quick & Dirty mit moves verpackt, da er ja Variable Längen und unterschiedliche Positionen zuläßt.
Die moves arbeiten intern auch mit Loops, somit ist es möglich, das diese Variante nicht unbedingt viel schneller ist.
Jedoch nehm ich der großen Loop(char by char) gleich die Luft raus, indem ich den offset auf die fertige String-Länge zurücksetzte. Und auch nicht mehr schauen muß, ob der Delimiter an der richtigen Position ist oder nicht, es ist garantiert!

Nun ist es aber wieder so, daß das Original leicht verstädlich ist... Und meines wieder nicht. Dennoch behaupte ich, diese Version ist der Version von Sir Rufo weit überlegen .... e.g. Pointer-Offsets!

Geändert von EgonHugeist (29. Jan 2016 um 05:05 Uhr) Grund: Anleitung hinzugefügt
  Mit Zitat antworten Zitat
EgonHugeist

Registriert seit: 17. Sep 2011
187 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: Leerzeichen in String einfügen

  Alt 28. Jan 2016, 14:56
@Sir Rufo,

der unangemeldeten Challange halber habe ich den vorherig geposteten Code mal gefixt. Habe mir die 8min genommen es zu testen.
Resultate:

Zitat:
Benchmarking DupeString('xyz', shl^0..10) Iterations: 2000000:
Autor: Sir Rufo Function: StrInsertEveryNthPos TickCount: 140922

Benchmarking DupeString('xyz', shl^0..10) Iterations: 2000000:
Autor: EgonHugeist Function: EH_StrInsertEveryNthPos TickCount: 25938
Auch hier wieder CountOfChar div APos length loop. Da hier alles variabel fallen die Benchmarks auch anders aus.

JFYI, includes the test, cheers.

Edit:
Nur mal so, hier ergibt sich ein irrsinninger Spread: desto größer APos oder AInsertStr desto weiter läuft meine Interpertation der deinen davon.

Habt Spaß beim Selber tüfteln.
Angehängte Dateien
Dateityp: pas QuadUnderScoreInjections.pas (11,2 KB, 4x aufgerufen)

Geändert von EgonHugeist (28. Jan 2016 um 17:55 Uhr) Grund: spread Kommentar
  Mit Zitat antworten Zitat
Antwort Antwort

 
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 17:34 Uhr.
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