Facets langsamer als normal



  • Was mir jedoch aufgefallen ist, ist, dass die Variante mit der Facet rund zweimal langsamer ist, als ersteres.

    Debug oder Release-Mode (mit/ohne Optimierung)

    Kann man eigentlich sagen, dass die IOstreams irgendwo performancemäßig für die Katz ist?

    Ja Streams sind langsam

    Ich kenne mich zwar nicht mit Assembler aus, aber der Code mit der Facet beträgt rund 5 Mal mehr Zeilen, als ohne.

    Assemblerlisting? - für Debug oder Release-Mode (mit/ohne Optimierung)?



  • Facette schrieb:

    ich habe zwei split()-Funktionen geschrieben, die einen vector<string> zurückgeben. Die erste Version habe ich mit einer selbstdefinierten ctype -Facet gemacht, die zweite Version ganz einfach mit stream.get(c); und dann auf c prüfen, ob es ein Delimiter ist.

    Was mir jedoch aufgefallen ist, ist, dass die Variante mit der Facet rund zweimal langsamer ist, als ersteres.

    Stell doch den Code mit Facette hier ein, vielleicht macht Du den Fehler in einer Schleife jedesmal use_facet<> zu rufen ...

    Facette schrieb:

    Warum also sollte ich hier Facets verwenden?
    Wo machen Facets denn eigentlich noch Sinn, wenn sie langsamer sind?

    das hat mit Geschwindigkeit nichts zu tun, sondern mit Austauschbarkeit. Mit Facetten kannst Du das Verhalten beim Lesen und Schreiben an die lokalen Gegebenheiten anpassen (z.B. Zeit, Datum, Dezimaltrenner, Währung).

    Facette schrieb:

    Kann man eigentlich sagen, dass die IOstreams irgendwo performancemäßig für die Katz ist?

    Das ist gefühlt tausendfach im I-net zu lesen. Es kommt i.A. immer darauf an WIE man sie benutzt und was man erwartet. Beispiel: https://www.c-plusplus.net/forum/310076-full



  • Ich mach das so:

    class joiner_ctype : public std::ctype<char>{
        mask table[table_size];
    
    public:
        joiner_ctype(const std::basic_string<char>& separators, std::size_t refs = 0)
            : std::ctype<char>(table, false, refs){
            std::copy_n(classic_table(), table_size, table);
    
            for(auto c : separators)
                table[(std::size_t)c] = (mask)space;
        }
    };
    
    std::vector<std::string> split(std::istream& stream, const std::string& delimiters){
        auto locale = stream.getloc();
        stream.imbue(std::locale(locale, new joiner_ctype(delimiters)));
    
        std::vector<std::string> parts;
        std::string s;
    
        while(stream >> s)
            parts.push_back(std::move(s));
    
        stream.imbue(locale);
    
        return parts;
    }
    


  • Ich kompiliere im Release-Modus mit -O2.



  • ich kann Deine Messung nicht nachvollziehen. Bei mir (Windows 7, Visual Studio 12) ist eine Version mit stream.get(c) fast zweimal langsamer.
    Datei ca. 140kB; Strings zwischen 4 und 10 Zeichen lang; Semikolon als Trenner
    mit facet: 6ms; mit stream.get(c): 11ms
    mit getline übrigens nur 4ms - Nachteil ist hier, dass nur ein Zeichen als Delimiter möglich ist.

    Wenn Du mehr als ein Zeichen als Delimiter zulässt, dann drängt sich bei mir der Verdacht auf, dass Du Deine Strings anschließend noch verarbeitest, bevor Du zu den Daten kommst, die Du wirklich benötigst. Wenn dem so ist, so ist das 'Einlesen' nach split noch nicht beendet!



  • Mist, ich hab falsch gemessen.
    Danke, das war der Fehler.
    Facets sind schneller.



  • HarteWare schrieb:

    Bin irgendwie hierauf gestoßen und ich finde das könnte interessant/relevant sein: http://info.prelert.com/blog/stdgetline-is-the-poor-relation
    P.S.: Ich kann keinerlei Aussagen über die Qualität der Quelle machen.

    Interessant wäre mMn. ein Vergleich zwischen dem dort geposteten Code und folgender kleinen Änderung:

    void populateVec(std::vector<std::string> &vec)
    {
        vec.clear();
        std::ifstream dataStrm("farequote.csv");
        std::string line;
        while (std::getline(dataStrm, line))
        {
            //vec.push_back(std::move(line));
            vec.push_back(line);
        }
    }
    

    Würde mich nicht wundern wenn das dadurch deutlich schneller würde. Wenn ich nicht vergesse probier' ich das heute Abend mal aus.



  • So.
    Habe jetzt gemessen. Verdacht bestätigt. Die kopierende Version ist mit meinem Test-Code (VC++2015) und Test-File (1000 Zeilen mit zufälliger Länge bis ~300 Zeichen) um ca. 20% schneller als die movende.

    Ratespiel: Wer weiss wieso?



  • hustbaer schrieb:

    So.
    Habe jetzt gemessen. Verdacht bestätigt. Die kopierende Version ist mit meinem Test-Code (VC++2015) und Test-File (1000 Zeilen mit zufälliger Länge bis ~300 Zeichen) um ca. 20% schneller als die movende.

    Ratespiel: Wer weiss wieso?

    Ich vermute das liegt an std::getline wenn diese methode zeichenweise elemente zum string hinzufügt gibt es öfters reallokationen.
    Durch das move wird die std::string instance "line" komplett "leer".

    Eine kopie des string ist vermutlich sehr effizient implementiert (eventuell als allokation + memcopy implementiert)

    Bei deiner Variante behält line seinen internen buffer -> bei 2 und folgenden aufrufen von std::getline werden gar keine oder nur sehr wenige reallokationen notwendig. Wenn in deinem Beispiel vom programm einmal eine 300 zeichen zeile eingelesen wurde (welches bei dir das maximum ist), so muss std::string später keinerlei reallokation mehr machen.

    Was ich mir noch vorstellen könnte, das eventuell eine variante mit std::vector::emplace_back statt std::vector::push_back bei deiner variante nochmal minimal schneller ist.



  • Ja, so würde ich das auch interpretieren 👍



  • emplace_back bringt keine weitere (messbare) Verbesserung.


Anmelden zum Antworten