Geschwindigkeitsfrage Datei einlesen und konvertieren



  • 567546546 schrieb:

    manni66 schrieb:

    Welchen Sinn hat es, die eingelesenen Zeilen zu speichern?

    Das kommt dann wohl auf den Anwendungszweck an.

    Ach



  • 1. Warum der Umweg über getline/string/stoi, du könntest doch auch gleich int lesen. Und dann ein einzelnes Zeichen, das dann # oder \n sein muss.
    2. Was machst du mit den Daten überhaupt? Du hast mehrere Zahlen pro Zeile und mehrere Zeilen pro Datei. Soll da ein vector<vector<int>> rauskommen oder kannst du gleich etwas berechnen?
    3. Oder haben gar alle Zeilen dieselbe Anzahl Zahlen drin und Spalten.size == klein? Dann kann es sinnvoll sein, alles in einem vector<int> v(nSpalten*nZeilen) zu speichern (sofern die Daten denn alle gespeichert werden müssen).

    Aber "Das kommt dann wohl auf den Anwendungszweck an.".



  • @camper Dachte ich eigentlich schon aber ich versuch mich zu bessern 🙂 Mir geht es nicht um falschen Code sondern um Erfahrungswerte, denn ich kann mir gerade nicht vorstellen warum

    [b]globle Definition[/b]
    std::vector<std::string>	m_v_zeilen;
    
    [b]loakle Definition[/b]
    std::string l_str;
    std::ifstream l_importdatei("Beispieldatei.dat", std::ios_base::binary);
    while ((l_importdatei.good()) && (getline(l_importdatei, l_str))){ m_v_zeilen.push_back(l_str); }
    l_importdatei.close();
    

    soviel langsamer ist wie

    [b]globle Definition[/b]
    std::string m_v_zeilen[1000000];
    
    [b]loakle Definition[/b]
    std::string l_str;
    std::ifstream l_importdatei("Beispieldatei.dat", std::ios_base::binary);
    int i= 0;
    while ((l_importdatei.good()) && (getline(l_importdatei, m_v_zeilen[i]))){ i++;}
    l_importdatei.close();
    

    Ich hoffe mit den Beispiel gefällt dir die frage besser 😃

    @67695563 Das einlesen ist notwendig, da die Daten aus einen autarken System abgezogen werden das aus Sicherheitsgründen nicht am I-Net hängt. Das System ist nicht mehr das jüngste und liefert alles als Textdatei. 😞

    @manni66 Wo hab ich geschrieben das ich die eingelesenen Zeilen speichere? Ich hab geschrieben das ich eine Datei einlese und die Daten muss ich ja erstmal in eine Datenstruktur schieben. 🙂



  • Ich wette, dass du noch andere Dinge machst bzw. uns verschweigst. Wie schaffst du es sonst, 60 Sekunden für das Einlesen zu brauchen? Oder sind deine Zeilen extrem lang? Ich bin jetzt mal von 80 Zeichen/Zeile mal 1 Mio Zeilen = 80 MB an Daten ausgegangen. Das sollte in unter einer Sekunde passieren (zumindest wenn das alles im Cache ist).



  • Julian90 schrieb:

    @manni66 Wo hab ich geschrieben das ich die eingelesenen Zeilen speichere? Ich hab geschrieben das ich eine Datei einlese und die Daten muss ich ja erstmal in eine Datenstruktur schieben. 🙂

    Wozu? Was muss mit den Daten gemacht werden? Die Frage von manni66 zielt doch offensichtlich darauf, dass es auf die genaue Anforderung ankommt, ob die Daten alle gleichzeitig im Speicher verfügbar sein müssen 🙄
    Außerdem bleibt die Frage, warum liest Du nicht gleich int-Werte ein, warum öffnest Du die Datei mit std::ios_base::binary ... was wird schließlich mit den int-Werten gemacht?



  • Damit du mal einen Richtwert hast, 1 Mio. Zeilen mit je 20 Zahlen einlesen/splitten/konvertieren dauert bei mir eine Sekunde, mit (unnötigem) vorherigen Speichern der Zeilen in einem vector 1.15 Sekunden. Mit etwas Optimieren (beim Splitten) sollte sich diese Zeit auf die Hälfte reduzieren lassen.

    Da läuft bei dir also etwas grundlegend schief, es sei denn, deine Zeilen enthalten tausende Zahlen. Naheliegende Idee: sind überhaupt Optimierungen eingeschalten und kompilierst du im Release-Modus?

    Außerdem ist std::stoi sehr ineffizient. boost::lexical_cast/atoi sind nur wenig schneller, ein vernünftiges stoi musst du also vermutlich selbst implementieren. Aber vorher sind da noch andere Baustellen.



  • Nachtrag: das mit std::stoi bezog sich auf gcc/libstdc++. Das kann bei anderen Implementierungen der Standardbibliothek natürlich anders aussehen.



  • Bzgl. Warum

    std::vector<std::string>	m_v_zeilen;
    

    soviel langsamer ist wie

    std::string m_v_zeilen[1000000];
    

    Viele vector::push_backs sind schlecht, da dauernd reallokiert werden muss.
    Verwende evtl. vector::reserve zur Optimierung.

    Edit: Vergesst es. Die Std-implementierung sollte eigentlich schlau genug sein um zu vielle unnötige Allocs zu verhindern.
    Siehe hierzu zB http://www.drdobbs.com/c-made-easier-how-vectors-grow/184401375



  • Wenn du einen Richtwert für die größe deines Vektors hast, benutze

    std::vector<std::string>    m_v_zeilen;
    m_v_zeilen.reserve(1000000);
    

    Damit reservierst du entsprechenden Speicher. Ansonsten muss für push_back immer entsprechend Speicher neu reserviert werden und dass dauert eine gewisse Zeit.

    Je nach dem, was du mit den Daten vor hast, musst du die Daten auch nicht in eine Daten Struktur speichern. Du liest die erste Zahl ein, berechnest mit der irgendwas, machst mit dem Ergebnis was auch immer dir gefällt und liest dann die nächste Zahl ein.

    //Edit: scrontch war schneller bezgl reserve



  • Schlangenmensch schrieb:

    Damit reservierst du entsprechenden Speicher. Ansonsten muss für push_back immer entsprechend Speicher neu reserviert werden und dass dauert eine gewisse Zeit.

    Das stimmt zwar, aber gerade bei Typen die trivial movable sind (wie std::string) handelt es sich hier um wenige Prozent Performanceunterschied.
    Ich tippe hier auf so etwas wie angeschaltene Debug-Iteratoren.



  • Julian90 schrieb:

    @manni66 Wo hab ich geschrieben das ich die eingelesenen Zeilen speichere? Ich hab geschrieben das ich eine Datei einlese und die Daten muss ich ja erstmal in eine Datenstruktur schieben. 🙂

    Da

    Julian90 schrieb:

    Erste Idee das einlesen mit std::ifstream und getline in ein std::string Array.

    Da

    Julian90 schrieb:

    [b]globle Definition[/b]
    std::vector<std::string>	m_v_zeilen;
    


  • wob schrieb:

    Wie schaffst du es sonst, 60 Sekunden für das Einlesen zu brauchen?

    Das sollte problemlos möglich sein, wenn man den Optimizer nicht einschaltet.



  • Es wurde ja schon angedeutet, aber ich zeige es mal im Code:

    std::vector<int> data;
    char dummy;
    std::ifstream in("file.dat");
    while (true)
    {
      int i;
      if (in >> i)
        data.push_back(i);
      in >> dummy;   // skip '#'
    }
    

    Also nicht zeilenweise einlesen und dann nochmal über atoi oder istringstream oder sonst was, sondern direkt aus der Datei lesen. std::ifstream ist genau so von istream abgeleitet wie istringstream.



  • tntnet schrieb:

    Es wurde ja schon angedeutet, aber ich zeige es mal im Code:

    std::vector<int> data;
    char dummy;
    std::ifstream in("file.dat");
    while (true)
    {
      int i;
      if (in >> i)
        data.push_back(i);
      in >> dummy;   // skip '#'
    }
    

    Wann hört die Schleife wohl auf? Was ist mit Fehlern?



  • manni66 schrieb:

    wob schrieb:

    Wie schaffst du es sonst, 60 Sekunden für das Einlesen zu brauchen?

    Das sollte problemlos möglich sein, wenn man den Optimizer nicht einschaltet.

    Mit welchem Compiler?

    Gerade mal getestet:
    Erzeugen der Datei:

    perl -E 'sub r{int(rand(100000000))} for $row(1..1000000){print r,"#" for 1..10; say r}' > 1-mio-lines.txt
    

    Testprogramm

    #include <string>
    #include <fstream>
    #include <iostream>
    #include <vector>
    
    int main() {
        std::vector<std::string> m_v_zeilen;
        std::string l_str;
        std::ifstream l_importdatei("1-mio-lines.txt");
        while (getline(l_importdatei, l_str)) {
            m_v_zeilen.push_back(l_str);
        }
        if (!l_importdatei.eof()) std::cout << "Kein eof!\n";
        l_importdatei.close();
        std::cout << m_v_zeilen.size() << '\n';
    }
    

    Also die Vector-Version.

    Und dann

    g++ -O0 -Wall -Wextra read.cpp
    time ./a.out          
    1000000
    ./a.out  0,17s user 0,06s system 99% cpu 0,226 total
    

    Weit weg von 60s.



  • wob schrieb:

    manni66 schrieb:

    wob schrieb:

    Wie schaffst du es sonst, 60 Sekunden für das Einlesen zu brauchen?

    Das sollte problemlos möglich sein, wenn man den Optimizer nicht einschaltet.

    Mit welchem Compiler?

    Also Visual Studio sollte das schaffen, besonders wenn man das Programm aus der IDE startet.



  • manni66 schrieb:

    Wann hört die Schleife wohl auf? Was ist mit Fehlern?

    Ja, richtig. Also falsch. Habs so hin geschmiert. Na sollte aber keine große Herausforderung sein, das zu ergänzen. Das Prinzip sollte aber klar sein.



  • Hallo Julian,

    Versuche mal unten stehenden Code. Damit schaffe ich 80Millionen 4-stellige Integer-Werte (Datei = 40MB groß) in 0,6 Sekunden in einen std::vector zu schaufeln. Das Lesen selbst liegt bei ca. 0,5s und der std:vector kostet 0,1s.
    Du könntest alternativ eine std::deque<> verwenden.

    Julian90 schrieb:

    ... die Daten muss ich ja erstmal in eine Datenstruktur schieben

    Nö - nicht 'erstmal' sondern gleich in das Ziel. Daher blebt die Frage nach der entgüligen Verwendung der Daten. Das ist auch entscheidend für die Art&Weise des Einlesens.

    #include <fstream>
    #include <iostream>
    #include <chrono>
    #include <locale>   // std::num_get<>
    #include <vector>
    
    class NumGet : public std::num_get< char >
    {
    public:
        using iter_type = std::istreambuf_iterator< char, std::char_traits< char > >;
        NumGet()
            : std::num_get< char >()
        {}
    
    protected:
        iter_type do_get( iter_type in, iter_type end, std::ios_base&,
            std::ios_base::iostate& err, long& v) const override
        {
            // Achtung: liest nur positive Werte
            long l = 0;
            for( ; in != end; ++in )
            {
                if( '0' <= *in && *in <= '9' )
                    (l *= 10) += int(*in - '0');    // Achtung: KEIN Check auf overflow
                else
                {
                    v = l;
                    return in;
                }
            }
            err |= std::ios_base::failbit;
            return in;
        }
    };
    
    class Alles
    {
    public:
        using target_type = std::vector< int >;
        Alles( target_type& target )
            : target_( target )
        {}
    
        friend std::istream& operator>>( std::istream& in, const Alles& all )
        {
            std::istream::sentry ok( in );
            using Traits = std::istream::traits_type;
            if( ok )
            {
                std::ios_base::iostate state = std::ios_base::goodbit;
                const NumGet num_get;
                try
                {
                    const std::ctype< char >& ctype_ = std::use_facet< std::ctype< char > >( in.getloc() );
                    std::istreambuf_iterator< char > last;
                    for(;;)
                    {
                        // --   skip leading whitespace
                        Traits::int_type m = in.rdbuf()->sgetc();
                        for( ;; m = in.rdbuf()->snextc() )
                        {
                            if( Traits::eq_int_type( m, Traits::eof() ) )
                            {
                                state |= std::ios_base::eofbit;
                                break;
                            }
                            else if( !ctype_.is( std::ctype_base::space, Traits::to_char_type(m) ) )
                                break;    // kein whitespace
                        }
                        if( state == std::ios_base::eofbit )
                            break;  // Lesen am Dateiende ohne Fehler beenden
    
                        // --   Integer einlesen
                        long wert = 0;
                        num_get.get( std::istreambuf_iterator< char >( in.rdbuf() ), last, in, state, wert );
                        if( state != std::ios_base::goodbit )
                            break;
    
                        all.target_.push_back( wert );  // <====== was soll mit den Werten geschehen?
                    }
                }
                catch(...)
                {
                    state |= std::ios_base::badbit;
                    if( in.exceptions() & std::ios_base::badbit )
                        throw;
                }
                in.setstate( state );        
            }
            return in;
        }
    
    private:
        target_type& target_;
    };
    
    int main()
    {
        using namespace std;
        ifstream in("big.txt");
        auto start = chrono::steady_clock::now();
        std::vector< int > daten;
        if( !(in >> Alles(daten)) )
            cerr << "Fehler beim Lesen" << endl;
        auto ende = chrono::steady_clock::now();
        cout << "Dauer: " << chrono::duration_cast< chrono::milliseconds >(ende-start).count() << "ms" << endl;
        cout << "Anzahl= " << daten.size() <<endl;
        return  0;
    }
    

    Gruß
    Werner


Anmelden zum Antworten