Resourcenproblem mit std::stringstream



  • Hallo zusammen,

    ich habe eine Frgae zur Verwendung der std::stringstream klasse aus dem C++ Standard.

    Ich stelle einen umfangreichen stringstream zusammen (1 MB) und möchte auf den darin enthaltenen std:string zugreifen, da ich eine Library verwende die ein std::string Objekt haben möchte.

    Mit der Methode str() des Streams wird jedoch eine Kopie des internen std::string Objekts erstellt und damit noch einmal der gleiche Speicher alokiert. Diese Resourcen-Verschwendung möchte ich umgehen.

    Ich habe bisher keine offizielle Möglichkeit gefunden auf das string-Objekt zuzugreifen. 😡

    Kennt sich jemand mit diesem Thema aus ?

    Danke.



  • Bist du dir sicher, das es eine Kopie gibt? Wie hast du das überprüft?
    Es steht zwar im Standard drin, das formal eine Kopie zurück gegeben wird (was auch sinnvoll ist, weil man den String nunmal nicht direkt manipulieren darf). Ein moderner Compiler kann aber meistens RVO anwenden und so Kopien vermeiden, wenn möglich und das Ergebnis nicht ungültig macht. Und RVO ist auch im Standard definiert und nicht illegal!

    Infos zu RVO:
    http://en.wikipedia.org/wiki/Return_value_optimization

    Von MS gibts da auch einen RVO-Artikel:
    http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx



  • Hast du einen speziellen Grund warum du diese veremintliche Ressourcenverschwendung umgehen möchtest, obwohl du keinen offensichtlichen Weg in den Methoden des stringstreams gefunden hast? Verlass dich doch erstmal auf die Optimierungskünste deines Compilers und schau hinterher, wenn du tatsächlich Ressourcenprobleme hast, nach, wo die sind, bevor du versuchst irgendwelche möglichen Optimierungen zu suchen.



  • pro_develop schrieb:

    Ich habe bisher keine offizielle Möglichkeit gefunden auf das string-Objekt zuzugreifen. 😡

    Dazu muß ich noch mal was speziell anmerken: zur Objektorientierung gehört auch Datenkapselung. Und das was du forderst, widerspricht der Datenkapselung. Es ist somit richtig das es keine direkten Zugriffsmöglichkeiten gibt und "nur" eine Kopie zurück gegeben wird, wenn man an die Rohdaten will.



  • Danke für die vielen und schnellen Antworten 🙂

    ... also so wie ich das unter der Speicherauslastung im Taskmanager gesehen habe wird der Speicher neu alokiert ... es müsste mit str() ja auch der Kopierkonstruktor des std::string Objekts aufgerufen werden ...

    der Kommentar mit der Kapselung ist natürlich richtig, aber bei den anderen Streamarten werden ja auch vorhandene Datenquellen in den stream eingebettet ...
    bei stringstream ist das jedoch nicht möglich, da dem Konstruktor eine Referenz auf std::string übergeben werden kann, aber intern trotzdem eine Kopie des string-Objekts erstellt wird ...

    Kennt sich jemand mit der boost-library aus ? ... gibt es dort etwas brauchbares, das dieses problem gelöst hat ?



  • @Artchi

    Ich denke nicht, das RVO in meinem beschriebenen Fall angewendet wird... das std::string objekt hat doch eher eine kleine größe und beinhaltet einen pointer, an dem der alokierte Speicher hängt ... RVO wird doch nur bei großen Objekten einer Klasse verwendet ... oder nicht ?



  • pro_develop schrieb:

    ... also so wie ich das unter der Speicherauslastung im Taskmanager gesehen habe wird der Speicher neu alokiert

    Nur dass der Taskmanager dir nicht direkt wieviel Speicher dein Programm wirklich braucht und noch weniger, was da tatsächlich so viel Speicher verbraucht - er kann dir nur erzählen was dein Betriebssystem den Programm an Speicher zugewiesen hat, und das ist bei weitem nicht das selbe.



  • Artchi schrieb:

    Es steht zwar im Standard drin, das formal eine Kopie zurück gegeben wird (was auch sinnvoll ist, weil man den String nunmal nicht direkt manipulieren darf). Ein moderner Compiler kann aber meistens RVO anwenden und so Kopien vermeiden, wenn möglich und das Ergebnis nicht ungültig macht. Und RVO ist auch im Standard definiert und nicht illegal!

    RVO könnte hier aber relativ schwierig werden; genauer gesagt ist selbst bei Anwendung von RVO noch eine Konstruktion notwendig. Falls der std::stringstream intern einen std::string speichert, muss er diesen kopieren, da es sich nicht um ein temporäres Objekt handelt. Ansonsten muss ein neuer std::string konstruiert werden. Was wegoptimiert werden kann, ist die unnötige Kopie beim Rückgabewert oder bei der Initialisierung.

    pro_develop schrieb:

    RVO wird doch nur bei großen Objekten einer Klasse verwendet ... oder nicht ?

    Mit Grösse hat Return Value Optimization erstmal nichts zu tun. RVO wird oft angewandt, wenn ein temporäres oder funktions-lokales (NRVO) Objekt zurückgegeben wird und die Kopie offensichtlich unnötig ist.

    Eine mögliche Lösung für dein Problem: Du kannst den std::string zuerst mit reserve() vorbereiten und anschliessend nach und nach per append() direkt füllen. Dann sparst du dir einen Schritt, nämlich die grosse Kopie von std::stringstream nach std::string . Die kleinen Kopien werden jetzt halt mit append() statt operator<< vollzogen.

    Übrigens ist es wahrscheinlich, dass bei der Übergabe an die Bibliotheksfunktion so oder eine Kopie erstellt werden muss. Aber falls die Funktion wirklich für grosse Strings konzipiert ist, warum arbeitet sie mit Objekten? Da wäre ein Zeiger-Herumreichen, im Optimalfall sogar mit Smart Pointers, weitaus günstiger.

    Edit: Denkfehler, der sich im Nachhinein als falsch herausstellte. 😉



  • @pumuckl:

    okay ... und mit welchen tools bzw. librarys könnte ich das noch besser untersuchen ?

    ... und stimmst du mir nicht bei der Antwort an Artchi zu ?

    @Artchi

    Ich denke nicht, das RVO in meinem beschriebenen Fall angewendet wird... das std::string objekt hat doch eher eine kleine größe und beinhaltet einen pointer, an dem der alokierte Speicher hängt ... RVO wird doch nur bei großen Objekten einer Klasse verwendet ... oder nicht ?



  • ...ich habe auch noch geantwortet, für den Fall, dass du das übersehen hast. 🙂



  • Wer sagt denn eigentlich, dass stringstream intern einen std::string verwendet? Er kann da auch ein simples char-Array (passend zu den traits) haben. Dann wird der string erst bei Aufruf von str() erzeugt. Das kann dann natürlich optimiert werden.



  • Braunstein schrieb:

    Dann wird der string erst bei Aufruf von str() erzeugt.

    So wie ich pro_develop verstanden habe, ist das genau sein Problem. Ob man von einem std::string oder char* eine Kopie erstellt, spielt schlussendlich keine so grosse Rolle. Es braucht beides Speicher und Rechenzeit, mit denen er sparsam umgehen wollte. 😉

    Braunstein schrieb:

    Das kann dann natürlich optimiert werden.

    Optimiert werden können auch hier wieder nur unnötige temporäre Kopien.

    std::stringstream Stream;      // mit 1 MByte füllen
    DoSomething(Stream.str());     // kopiert mindestens einmal 1 MByte
    


  • @Nexus:

    Habe deine umfangreiche Antwort natürlich nicht übersehen 🙂

    Ich habe mich auch bei den letzten Antworten falsch ausgedrückt ... ich meinte, dass RVO nur bei großen Objekten einer Klasse als Rückgabewert einen effektiven Nutzen haben dürfte... und dass ich nicht glaube, dass die Umsetzung von RVO so komplex ist, dass auch dynamisch alokierte Objekte von RVO gehandelt werden ....

    Ich verstehe auch einfach nicht das Prinzip, nach dem der C++ Standard die stringstreams umsetzt ... bei einer Datei oder den E/A-Geräten kann ich doch auch direkt etwas auf dem Bildschirm dazwischen ausgeben ... oder wenn ich auf die datei zugreifen möchte, dann muss ich doch auch nicht erst eine kopie erstellen ... ?

    Das mit der Datenkapselung ist schon richtig, aber es könnte doch wenigstens einen offiziellen Weg geben, um an die Adresse des internen string-Objekts zu gelangen um ggf. Zugriff auf die eigentlichen Rohdaten zu bekommen.

    Ich finde das einfach nicht durchgängig von der Umsetzung her ... gibt es bei dem C++ Gremium, das den Standard festlegt, soetwas wie ein Verbesserungsvorschlagswesen ? Hat da jemand Kontakte ? 😉



  • @Braunstein:

    Ich dachte das ist nur in std::strstream ein char Array ...

    In einem Artikel hatte ich gelesen, dass der Standard ein std::string Objekt für std::stringstream verwendet ...

    Ist die Info in dem Artikel etwa falsch ???



  • Wenn ich den Standard richtig lese, ist die interne Representation der Daten in stringstream nicht festgelegt. Es wird lediglich verlangt, dass mit einem basic_string initialisiert werden kann und dass eine Funktion zur Rückgabe eines solchen existiert.



  • pro_develop! Da hast du etwas falsch verstanden. Nehmen wir mal die Filestreams von C++. Auch dort entstehen natürlich Kopien, der Daten:

    ofstream f("datei.txt");
    f << "Hallo"; // landet im Stream-Puffer, ein gekapselter Speicherblock
    f.flush(); // eine Kopie des Stream-Puffers landet im Dateisystem (z.B. Festplatte)
    

    Bei allen Outputstreams schiebst du Daten rein, und die werden am Ende irgendwo als Kopie landen müssen. Gerade wegen der Kopiererei sind die meisten Streams gepuffert! Und ohne Puffer wäre es wohl noch langsamer, weil dann jedes Byte einzeln kopiert werden müsste, anstatt der effizienteren blockweisen Kopie (so groß wie der Puffer halt).

    Selbst die Ausgabe mit cout bedingt eine Kopie der Daten auf dem Bildschirmspeicher oder Terminal oder wie auch immer. Wenn durch flush() was in der Konsole oder Festplatte erscheint, ist es vergleichbar mit dem str() .

    Das die Streams einen Direktzugriff erlauben, kann ich nicht erkennen. Kannst du vielleicht eine Methode vom fstream nennen?

    Zu dem Gremium: das ist die WG21 Group. Und natürlich kann jeder dort seine Vorschläge einreichen (das Gremium bittet sogar darum!). Du kannst sogar an den Meetings persönlich als Zuschauer teilnehmen, die freuen sich wenn es Interessenten gibt. 🙂 Eine Anlaufstelle kann die Newsgroup news://comp.lang.c++.moderated sein, da lesen und schreiben die meisten aus dem Gremium mit.
    Tip: http://magazin.c-plusplus.net/artikel/Der CPlusPlus-Standardisierungs-Prozess



  • @Nexus:

    Danke für deinen Lösungsvorschlag 🙂

    ... da hab ich zu schnell auf "senden" gedrückt ... ich wollte doch noch auf deinen Lösungsvorschlag eingehen ...

    ... wenn ich zwischen dem stream und dem string objekt häppchenweise kopiere, dann habe ich doch aber das Problem mit dem allokierten Speicher nicht umgangen ??? ...

    wenn meine Datei 100 MB groß wird, dann müsste ich doch mit string.reserve() die ganzen 100 MB allokieren ... oder wie hast du das gemeint ?

    und der stream verringert ja auch nicht automatisch seine Größe, wenn ich in kleinen Stücken daraus lese ... ich könnte den stream doch erst zum Schluß dealokieren ... oder ?



  • pro_develop! Warum benutzt du denn stringstream ? Wenn du problemlos string nutzen kannst, war stringstream eh die falsche Klasse. Denn normalerweise erfüllen beide Klassen unterschiedliche Zwecke.



  • @Artchi:

    du hast nicht verstanden wie ich das meine 😞 ... ich versuch es noch einmal zu erklären ...

    wenn du einen File-Stream erzeugst, dann gibst du eine vorhandene, greifbare Datenquelle, also die auf der Platte vorhandene Datei an ... auf diese Rohdaten in der Datei kannst du auch auf anderen Wegen zugreifen ...

    Bei den String-Streams kannst du eine vorhandene Datenquelle angeben, auf die ein freier Zugriff existiert ... es wird jedoch intern eine Kopie erstellt und damit geht der Zugriff auf die vom Stream bearbeiteten Daten verloren ... im Gegensatz zu einer Datei ...



  • Und im Zweifelsfall kannst du dir immmernoch eine eigene Streamklasse schreiben, die exakt auf deine Anforderungen zugeschnitten ist. 😃


Log in to reply