Geschwindigkeitsfrage Datei einlesen und konvertieren



  • 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