Schnelles lesen von großen Dateien in den Speicher



  • Hallo Leute,

    ich wollte eine ascii matrizen in den Speicher laden. Leider ist mein Code (irgendwie) sehr langsam. Eigentlich sollte es dank SSD in wenigen Sekunden gehen was auch beim reinen Kopieren der Fall ist. Bremst der cast so stark aus? Wäre für jeden Tipp dankbar wie man es beschleunigen könnte.

    #include <string>
    #include <cstdlib> // Für EXIT_SUCCESS
    #include <fstream>
    #include <iostream>
    
    // Matrizenrechnung
    #include <boost/numeric/ublas/io.hpp>
    #include <boost/numeric/ublas/matrix.hpp>
    #include <boost/numeric/ublas/matrix_proxy.hpp> // Zeilenzuordnung
    
    // String tokenizer
    #include <boost/foreach.hpp>
    #include <boost/tokenizer.hpp>
    
    // Double Caster
    #include <boost/lexical_cast.hpp>
    
    // Abkürzungen
    using std::string;
    using std::ifstream;
    using namespace boost::numeric::ublas;
    
    int main(int arc, char **argv) 
    {
    	string line;
    	std::size_t row = 0;
    	boost::char_separator<char> sep("\t");
    
    	ifstream infile_N("../N_rand_10k_x_10k.txt",ifstream::in);
    	ifstream infile_w("../w_rand_10k_x_10k.txt",ifstream::in);
    
    	vector<double> v (10000);
    	matrix<double> N (10000,10000);
    
    	while( ! infile_N.eof() )
    	{
    		getline(infile_N,line);		
    		boost::tokenizer< boost::char_separator<char>> tokens(line,sep);
    
    		matrix_row<matrix<double> > mr (N, row); 
    
    		BOOST_FOREACH(string t, tokens)
    		{
    			double elem;
    
    			try
    			{
    				elem = boost::lexical_cast<double>(t);
    			}
    			catch (const boost::bad_lexical_cast&)
    			{
    				return EXIT_FAILURE;
    			}
    			catch ( ... )
    			{
    				return EXIT_FAILURE;
    			}
    
    			v[row] = elem;
    		}
    
    		mr = v;
    
    		row++;
    	}
    
    	infile_N.close();
    
    	matrix<double> w (10000,1);
    
    	row = 0;
    
    	while( ! infile_w.eof() )
    	{
    		getline(infile_w,line);		
    
    		try
    		{
    			w(row++,1) = boost::lexical_cast<double>(line);
    		}
    		catch (const boost::bad_lexical_cast&)
    		{
    			return EXIT_FAILURE;
    		}
    		catch ( ... )
    		{
    			return EXIT_FAILURE;
    		}
    	}
    
    	infile_w.close();
    
    	return EXIT_SUCCESS;
    }
    


  • Blinde Vermutungen, wo und warum es langsam ist, ist nicht so professionell. Am besten benutzt man für sowas einen Profiler, der einem das schön analysiert.

    Aber man kann natürlich auch erstmal so drauf schauen. Und da fällt mir getline auf. Beim Kopieren einer Datei per wird im optimalen Fall alles komplett gelesen und dann kopiert. Bei getline wird häppschenweise gelesen und dadurch der Buffer des OS oder Hardware nicht genutzt.

    Wenn das reine Lesen vom Speichermedium so schnell wie beim kopieren gehen soll, mußt du die Datei komplett lesen und dann erst analysieren.

    Das wäre so meine erste Vorkehrung/Versuch, wenn ich keinen Profiler habe.



  • Wenn ich nun dass hier probiere:

    infile_N.seekg(0,std::ios::end);
    std::streampos length = infile_N.tellg();
    infile_N.seekg(0,std::ios::beg);
    
    vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    

    bekomme ich bad_alloc in Zeile 4. Length ist 1500001881. Die Datei selbst nicht nicht allzu groß. 1.5GB müsste locker in den Speicher gehen.



  • wm schrieb:

    Wenn ich nun dass hier probiere:

    infile_N.seekg(0,std::ios::end);
    std::streampos length = infile_N.tellg();
    infile_N.seekg(0,std::ios::beg);
    
    vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    

    bekomme ich bad_alloc in Zeile 4. Length ist 1500001881. Die Datei selbst nicht nicht allzu groß. 1.5GB müsste locker in den Speicher gehen.

    Ok das hat sich auch erledigt mit /LARGEADDRESSAWARE
    Ich dachte eigentlich dass man diesen Flag nur ab 3GB braucht.



  • Jetzt muss ich doch nochmal fragen in wieweit sich getline verändert.

    infile_N.seekg(0,std::ios::end);
    std::streampos length = infile_N.tellg();
    infile_N.seekg(0,std::ios::beg);
    
    vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    
    std::stringstream localStream;
    localStream.rdbuf()->pubsetbuf(&buffer[0],length);
    
    while( getline( localStream , line ) )
    

    Im Moment ist es so dass line "" ist. Kann es sein dass die Zeile 9 so nicht in Ordnung ist?



  • vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    

    Ist nicht besonders vorteilhaft, wenn es dir darum geht, dass du schnell lesen willst.

    std::vector<char> buffer;
    buffer.reserve(length);
    

    Ist sicher besser.


  • Mod

    Mentras schrieb:

    vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    

    Ist nicht besonders vorteilhaft, wenn es dir darum geht, dass du schnell lesen willst.

    std::vector<char> buffer;
    buffer.reserve(length);
    

    Ist sicher besser.

    Nicht nur unnötig, sondern auch falsch. Und die erste Variante sollte unter dem schnellsten sein, was geht.

    edit: Und an den Threadersteller: Eine Datei in einen Stringstream lesaen geht auch ohne Umwege:

    stringstream parser;
    ifstream fiel;
    file >> parser;
    


  • SeppJ schrieb:

    Nicht nur unnötig, sondern auch falsch. Und die erste Variante sollte unter dem schnellsten sein, was geht.

    Dann scheine ich die Dokumentation nicht zu verstehen.

    Der Konstruktoraufruf vegrößert den vector um length Bytes und initialisiert jedes Element mit 0 (Auf sein Beispiel bezogen). Also das Äquivalent zu .resize() Methode.

    Deswegen dachte ich mir:

    Einen riesen großen vector (1500001881 Bytes) mit Nullen zu initialisieren ist sicher langsamer als bloß den Speicher dafür zu reservieren.


  • Mod

    Mentras schrieb:

    SeppJ schrieb:

    😮 Was? 😕 😕 😕

    Einen riesen großen vector (1500001881 Bytes) mit Nullen zu initialisieren ist sicher langsamer als bloß den Speicher dafür zu reservieren.

    Und wenn du dann drauf zugreifst, kracht es.



  • SeppJ schrieb:

    Und wenn du dann drauf zugreifst, kracht es.

    Stimmt. Daran habe ich nicht gedacht.
    Danke!



  • Mentras schrieb:

    SeppJ schrieb:

    Und wenn du dann drauf zugreifst, kracht es.

    Verstehe ich nicht, könntest du es mir bitte erklären? 😮

    Mit std::vector<..>::reserve(..) wird nur der Speicher reserviert, d.h. std::vector<..>::capacity() verändert, nicht aber std::vector<..>::size().



  • SeppJ schrieb:

    stringstream parser;
    ifstream fiel;
    file >> parser;
    

    Das habe ich als erstes ausprobiert, funktioniert so leider nicht.



  • wm schrieb:

    SeppJ schrieb:

    stringstream parser;
    ifstream fiel;
    file >> parser;
    

    Das habe ich als erstes ausprobiert, funktioniert so leider nicht.

    Nein wie doof es geht exakt andersherum.

    std::stringstream << std::ifstream
    


  • Geht das ganze auch irgendwie ASCII? Irgendwie bekomme ich nur "0040F7D0"


  • Mod

    wm schrieb:

    Geht das ganze auch irgendwie ASCII? Irgendwie bekomme ich nur "0040F7D0"

    Was genau? Stell die Frage mal so, dass man nicht den kompletten Thread lesen muss, um sie zu verstehen.



  • Kurze Frage: Ist die Anzahl der Werte je Zeile immer gleich?



  • Tachyon schrieb:

    Kurze Frage: Ist die Anzahl der Werte je Zeile immer gleich?

    Ich habe verschiedene Dateien mit Matrizen unterschiedlicher Größe. Je Datei sind bleibt die Größe konstant.

    SeppJ schrieb:

    Stell die Frage mal so, dass man nicht den kompletten Thread lesen muss, um sie zu verstehen.

    mit localStream << inFile_N;
    
    while( getline( localStream , line ) )
    {
    

    kann ich getline nutzen aber die ausgelesenen Werte scheinen nicht ascii codiert zu sein.

    "0027F728ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««îþîþ"
    "0027F728ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««îþîþ"
    

    Wenn ich aber:

    vector<char> buffer(length,0);
    infile_N.read(&buffer[0],length);
    
    std::stringstream localStream;
    localStream.rdbuf()->pubsetbuf(&buffer[0],length);
    
    while( getline( localStream , line ) )
    {
    

    benutze dann ist der Inhalt richtig aber getline bringt immer wieder einen leeren string.



  • wm schrieb:

    Tachyon schrieb:

    Kurze Frage: Ist die Anzahl der Werte je Zeile immer gleich?

    Ich habe verschiedene Dateien mit Matrizen unterschiedlicher Größe. Je Datei sind bleibt die Größe konstant.

    Dann vielleicht so als primitiver Ansatz:

    int main()
    {
        std::size_t columns = 0;
        //Anzahl Spalten ermitteln
        {
            std::ifstream reader("N_rand_10k_x_10k.txt");
            std::string line;
            std::getline(reader, line);
            std::istringstream isstr(line);
            double dummy;
            while(isstr >> dummy)
            {
                ++columns;
            }
        }
        //Hier beginnt das eigentliche Einlesen
        std::ifstream reader("N_rand_10k_x_10k.txt");
        std::vector<double> row(columns);
        std::size_t current_column = 0;
        std::size_t current_row = 0;
        while(reader >> row[current_column])
        {
            ++current_column;
            if(current_column == columns)
            {
                current_column = 0;
                //assign to matrix here
                ++current_row;
            }        
        }
    
    }
    

  • Mod

    Ach, oben habe ich bei dem Beispiel auch das rdbuf() vergessen. 'Tschuldigung. So geht das:

    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
      ifstream infile("test.cc");
      stringstream buffer;
      buffer << infile.rdbuf();
      string line;
      while (getline(buffer,line))
        cout << line << '\n';
    }
    


  • Ich habe in der Zwischenzeit mal mapped_file ausprobiert

    #include <string>
    #include <cstdlib> // Für EXIT_SUCCESS
    #include <iostream>
    
    #include <boost/foreach.hpp>
    #include <boost/tokenizer.hpp>
    #include <boost/lexical_cast.hpp>
    #include <boost/numeric/ublas/io.hpp>
    #include <boost/iostreams/stream.hpp>
    #include <boost/numeric/ublas/matrix.hpp>
    #include <boost/numeric/ublas/matrix_proxy.hpp>
    #include <boost/iostreams/device/mapped_file.hpp>
    
    // Abkürzungen
    using boost::iostreams::stream;
    using namespace boost::numeric::ublas;
    using boost::iostreams::mapped_file_source;
    
    int main( int argc , char **argv ) 
    {
    	std::string line;
    	std::size_t row = 0;
    	boost::char_separator<char> sep( "\t" );
    
    	stream<mapped_file_source> infile_N(mapped_file_source("../N_rand_10k_x_10k.txt")); 
    
    	if ( ! infile_N.is_open() )
    	{
    		return EXIT_FAILURE;
    	}
    
    	stream<mapped_file_source> infile_w(mapped_file_source("../w_rand_10k_x_10k.txt"));
    
    	if ( ! infile_w.is_open() )
    	{
    		return EXIT_FAILURE;
    	}
    
    	vector<double> v ( 10000 );
    	matrix<double> N ( 10000 , 10000 );
    
    	while( getline( infile_N , line ) )
    	{
    		boost::tokenizer<boost::char_separator<char>> tokens( line , sep );
    
    		matrix_row<matrix<double> > mr ( N , row ); 
    
    		BOOST_FOREACH( std::string t , tokens )
    		{
    			double elem;
    
    			try
    			{
    				elem = boost::lexical_cast<double>( t );
    			}
    			catch ( const boost::bad_lexical_cast & )
    			{
    				return EXIT_FAILURE;
    			}
    			catch ( ... )
    			{
    				return EXIT_FAILURE;
    			}
    
    			v[row] = elem;
    		}
    
    		mr = v;
    
    		row++;
    	}
    
    	infile_N.close();
    
    	matrix<double> w ( 10000 , 1 );
    
    	row = 0;
    
    	while( ! infile_w.eof() )
    	{
    		getline(infile_w,line);		
    
    		try
    		{
    			w( row++ , 1 ) = boost::lexical_cast<double>( line );
    		}
    		catch ( const boost::bad_lexical_cast & )
    		{
    			return EXIT_FAILURE;
    		}
    		catch ( ... )
    		{
    			return EXIT_FAILURE;
    		}
    	}
    
    	infile_w.close();
    
    	return EXIT_SUCCESS;
    }
    

    Ist aber immer noch sehr langsam. Mir scheint als ob er diese mapped_file nicht wirklich in den Speicher lädt.


Anmelden zum Antworten