Optimierung mit Strings



  • Hallo zusammen

    Im aktuellen Projekt wurde ein Parser umgesetzt, der aus dem Inhalt einer Datei ein Objekt erzeugt. Dabei nutze ich leider sehr viele String-Operationen. Momentan nutze ich die Klasse AnsiString von Borland und dabei auch besonders häufig '+='.

    Nun weiß ich noch von Java, dass dort sowas durch einen StringBuffer optimiert wird, da dieser nicht immer wieder ein neues Objekt erstellt. Kann mir jemand sagen, ob es eine solche Klasse auch in C++ gibt und wie diese heißt?

    Zudem werden viele Casts in meinem Code gemacht. Dabei wird überwiegend bei Funktionsaufrufen aus Integern AnsiStrings gemacht (automatisch im Hintergrund). Kann das ein weiterer Punkt sein, wo ich Performance unnötig verbrauche? Durch einen Umbau könnte ich auch die Werte als AnsiStrings vorhalten (den Typ des Parameters kann ich nicht anpassen).

    Allgemein gesagt suche ich Tipps, wie ich eine möglichst hohe Performance erreiche, wenn ich viel mit Strings arbeiten muss (wobei 'viel' momentan im Bereich von z.B. 100.000 Zeilen liegt, daher könnten auch kleinere Tipps viel bringen). Ist vielleicht auch die Klasse AnsiString für solche Operationen nicht die beste?

    Ich bin für jeden Tipp dankbar, denn momentan liegt das Einlesen, Auswerten und Exportieren einer Datei bei ca. 12min, was definitiv zu lange ist.



  • Hallo

    Du kommst mit einen std::stringstream/fstream sowohl vom Code-Aufbau als auch von der Geschwindigkeit besser weg.

    bis bald
    akari



  • Das hängt aber von der jeweiligen STL-Implementation ab. Zumindest bei Filestream-Operationen gibt es große Unterschiede. Am schnellsten war hier der BCB6 mit STLPort. Sowohl RougeWave (BCB5) als auch Dinkumware (BDS2006) sind da deutlich langsamer.
    @akari
    hast du AnsiString mal mit der STL verglichen?



  • Hallo

    @ Braunstein : Direkt nicht. Mit der Geschwindigkeit bezog ich mich darauf

    Nun weiß ich noch von Java, dass dort sowas durch einen StringBuffer optimiert wird, da dieser nicht immer wieder ein neues Objekt erstellt. Kann mir jemand sagen, ob es eine solche Klasse auch in C++ gibt und wie diese heißt?
    ...
    Dabei wird überwiegend bei Funktionsaufrufen aus Integern AnsiStrings gemacht

    Ich weiß nicht genau, was IntToStr im Detail macht, aber bei

    stream << 10;
    

    sollte die Umwandlung mit minimalem temporären Aufwand erfolgen (der dann noch von der Implementation der STL abhängen kann)

    bis bald
    akari



  • @DarkGuardian
    Wichtig ist hier erstmal dein normaler Code. Versuche Kopien zu vermeiden (insbesondere bei Funktionsparametern) und nutze immer wenn es geht Referenzen. Versuche die Erzeugung von temporären Strings zu vermeiden.
    z.Bsp.

    // Hier mehrere temporäre AnsiStrings
    AnsiString test = "Teststring1" + test2 + " Nr.3" + IntToStr(54);
    // Hier nicht
    AnsiString test("Teststring1");
    test += test2;
    test += " Nr.3";
    test += IntToStr(54);
    

    Versuche vor allem rauszubekommen welcher Teil deines Programmes die meiste Zeit braucht. Wenn es das Einlesen von der Datei ist, teste verschiedene Varianten (VCL, Standard-C++, C-Funktionen, WindowsAPI).
    Am schnellsten, aber auch am komplexesten und fehleranfälligsten ist pures C.
    Zumindest beim BCB6 waren die C-Funktionen nicht viel schneller als Standard-C++. Beim BCB5 war da schon mal Faktor 10 dazwischen.
    Funktionen aus der WinAPI sollten bei sehr großen Dateien das meiste bringen.



  • Braunstein schrieb:

    // Hier mehrere temporäre AnsiStrings
    AnsiString test = "Teststring1" + test2 + " Nr.3" + IntToStr(54);
    // Hier nicht
    AnsiString test("Teststring1");
    test += test2;
    test += " Nr.3";
    test += IntToStr(54);
    

    Ich nutze eh die zweite Variante, wobei dabei aber doch trotzdem jeweils pro Zeile ein neues Objekt vom Typ AnsiString erzeugt wird. Ansich sollte aber schon im Vorfeld ein möglichst großer Speicherbereich bereitgestellt werden, der bei den Additionen gefüllt wird, damit nicht unnötig Zeit zum Erstellen neues Objekt verbraten wird.

    Zudem scheint es wichtig zu sein, dass ich den BCB6 nutze. Ich hätte zwar nicht gedacht, dass es dabei große Unterschiede gibt, aber so wie es sich hier liest, braucht ihr die Info.

    Ich bin leider noch nicht dazu gekommen, std::stringstream zu testen. Aber vom Namen und der Beschreibung her, sollte es sowas sein, wie ich es suche. Trotzdem bin ich natürlich froh über jeden weiteren Tipp bzgl. Optimierung beim Arbeiten mit Strings (welchen auch immer).



  • DarkGuardian schrieb:

    ... Ich nutze eh die zweite Variante, wobei dabei aber doch trotzdem jeweils pro Zeile ein neues Objekt vom Typ AnsiString erzeugt wird. ...

    Nicht unbedingt. Wenn erst in AnsiString umgewandelt werden muß, klar. Dies ist hier

    test += test2;
    

    aber nicht erforderlich.
    Am Wichtigsten ist es wohl erst einmal festzustellen, wo denn der Engpass ist.



  • Ist es denn nicht so, dass durch ein '+=' das Objekt AnsiString mehr Speicher anfordern und ggfs den eigenen Inhalt dabei umkopieren muss? Da würde dann ja schon eine Verschwendung von Ressourcen beginnen.

    Leider kenne ich mich mit den Interna von C++ und der Bibliotheken nicht so gut aus, aber aus Java weiß ich, dass es dort so gemacht wurde (keine Ahnung, ob das noch immer der Fall ist). Daher suche ich ja einen passenden Buffer (oder etwas Vergleichbares, um optimiert arbeiten zu können).

    Trotzdem natürlich danke für deine Tipps. Wahrscheinlich muss ich einfach mal ausprobieren, womit ich Geschwindigkeitsvorteile erreichen kann.



  • Wie AnsiString das konkret handhabt weiß ich auch nicht. Intern werden ja Pascal-Funktionen aufgerufen (bei += LStrCat). Du könntest ja mal mit SetLength experimentieren. Wenn du Lust hast kannst du ja mal in die dstring.cpp sowie in die System.pas reinschauen. Da ist der Sourcecode für diese Funktionen zu finden. Ist aber viel ASM.
    Zuweisungen bei AnsiString verursachen übringens erstmal keine Kopien, da hier mit Referenzzählung gearbeitet wird.
    Theoretisch könnte der String ja grundsätzlich mehr Speicher vorhalten (so wie bei vector, deque etc.), so daß nicht so oft umkopiert werden muß.



  • hi

    @DarkGuardian

    wie ist die datei die du einlesen willst aufgebaut ?
    und wie sieht so ein object aus ?
    erstellst du die datei auch selber ?
    oder schreibst du gerade einen konverter ?

    kannst du da mal ein bisschen was davon zeigen ?

    Meep Meep



  • Es handelt sich um eine Logdatei, die etwas aufgebohrt ist. Darin enthalten sind Fehler- und Hinweistexte in drei verschiedenen Sprachen sowie die Logeinträge. Leider kann ich davon hier nichts zeigen, aber beschreiben kann ich es zumindest.

    Die Datei wird zeilenweise eingelesen, wobei zuerst die Texte genommen werden. Diese werden in unterschiedliche Kontainer geschoben (pro Sprache einen). Danach folgen die Logeinträge, die binär kodiert sind. Diese werden jeweils in kodierter Form in einer Struktur gesichert. Alle Logeinträge landen dann in einem Vector.

    Die einzelnen Bereiche sind durch definierte Texte (zB begin_errtext) in einer Zeile markiert. Dadurch kann meine Software unterscheiden, was die aktuelle Zeile darstellt.

    Sobald die Logdatei zur Anzeige gebracht werden soll, werden die Einträge umkodiert, damit sie lesbar in GUI dargestellt werden können.

    Im Endeffekt erhalte ich den Inhalt der Logdatei von einer DLL und bin der einzige, der dieses in eine Datei schreib und wieder ausliest. Bisher habe ich nur deswegen direkt die Werte aus der DLL in die Datei geschrieben, um dort keinen unnötigen Aufwand zu betreiben.

    Wie gesagt, kann ich leider nichts genaueres hier zeigen. Aber der Ablauf dürfte klar sein.

    @Meep Meep
    Helfen dir diese Infos weiter, um mir einen Tipp zur Optimierung zu geben?



  • re

    DarkGuardian schrieb:

    Im Endeffekt erhalte ich den Inhalt der Logdatei von einer DLL und bin der einzige, der dieses in eine Datei schreib und wieder ausliest. Bisher habe ich nur deswegen direkt die Werte aus der DLL in die Datei geschrieben, um dort keinen unnötigen Aufwand zu betreiben.

    also wenn ich dich richtig verstanden habe, dann erhaelst du zuerst die daten von einer dll, shreibst sie in eine datei, und dann willst du (jetzt kommt dein problempunkt) die datei wieder auslesen und irgendwo anzeigen.

    ansich kann man da nicht viel dazu sagen ohne source. ih wollte ja auch nicht direkt die log-daten sehen.
    bissl source waere aber nicht schlecht.
    wie sieht die binaere struktur aus, wie die fehler und hinweistexte. also damit meine ich, wie der code aussieht, der dir das zeugs in die datei meiselt.

    fuer 100.000 zeilen 12 minuten ist wirklich katastrophal lahm.
    naja haengt auch davon ab wie lange die zeilen sind. sollte aber auch nicht wirklich lange dauern.

    kannst vielleicht wenigstens den source fuer die struktur der datei mal posten?

    Meep Meep



  • Wie ich schon mal gesagt habe wäre es wichtig zu wissen was genau da so lange dauert. Das Einlesen, das Parsen oder was auch immer? Evtl. hilft es die Datei erst in den Speicher zu packen (evtl. auch stückchenweise) und dann zu parsen.


Anmelden zum Antworten