Schreiben in Ascii optimieren



  • Hallo Lummel202,

    Du hast eine ziemlich große Menge Daten zu lesen und zu schreiben. Die 47 Dateien müssten so jede ca. 20MB groß sein. Ich rechne pro MB formatiertes lesen etwa 1s - was nicht viel ist. Macht 20s pro Datei - macht knapp 16min nur für das Lesen.
    Du schreibst etwa die gleiche Menge - Schreibgeschwindigkeit ist - wenn überhaupt - 2mal schneller als lesen. Macht alles zusammen ca. 24min ... so über den Daumen, ohne die wahre Größe Deiner Dateien und die Leistungsfähigkeit Deiner Festplatte zu kennen.

    Das einzige, was mir noch einfällt, ist auf das Parsen und Schreiben der double-Werte zu verzichten.
    Du kannst die Zeile 28/29 ändern in:

    {   double a=0; std::string rest_der_zeile;                        
                    getline( infile >> a >> ws, rest_der_zeile );                        // Einlesen der Roh-Daten
    

    und dann entsprechend Zeile 50

    {outfile << setprecision (15) << timestemp << "\t" << rest_der_zeile << "\t\t" << i << "\n" ;}
    

    ... probier's mal aus und berichte von Deinen Erfahrungen.

    Gruß
    Werner



  • Werner:
    Wäre hier nicht wieder ein guter Trick statt operator>> eine eigene Version zu schreiben, die nur ein einzelnes sentry-Objekt pro Lesevorgang aus der Datei erzeugt? In deiner Version hast Du ja auch ein sentry pro Zeile. Könnte man nicht sogar eine Funktion schreiben, die gleichzeitig für Output und Input ein sentry erzeugt und die Zeichen dann gar nicht zwischenspeichert, sondern den sget() direkt sput()ted?

    Vielleicht ist das für OP zu viel und es geht hier auch nicht um meine Frage, aber in die Richtung hätte ich jetzt gedacht.



  • Hallo Eisflamme,

    Eisflamme schrieb:

    Werner:
    Wäre hier nicht wieder ein guter Trick statt operator>> eine eigene Version zu schreiben, die nur ein einzelnes sentry-Objekt pro Lesevorgang aus der Datei erzeugt? In deiner Version hast Du ja auch ein sentry pro Zeile. Könnte man nicht sogar eine Funktion schreiben, die gleichzeitig für Output und Input ein sentry erzeugt und die Zeichen dann gar nicht zwischenspeichert, sondern den sget() direkt sput()ted?

    Vielleicht ist das für OP zu viel und es geht hier auch nicht um meine Frage, aber in die Richtung hätte ich jetzt gedacht.

    Ich weiß schon auf welchen Thread Du Bezug nimmst. Na ja - mach' Du mal, ich gehe jetzt in's Bett.

    Ansonsten - beachte den Kommentar zu meinem eigenen Beitrag.
    Gutes Nächtle 😉
    Werner



  • Puh, also ich habe für den Anfang Mal eine copy-Funktion erstellt, das Parsen muss man jetzt natürlich noch komplett reinstecken.

    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <ctime>
    
    using namespace std;
    
    template<typename E_in, typename Traits_in, typename E_out, typename Traits_out>
    void fastCopy(basic_istream<E_in, Traits_in>& in, basic_ostream<E_out, Traits_out>& out)
    {
    	typedef basic_istream<E_in, Traits_in> istream_t;
    	typedef basic_ostream<E_out, Traits_out> ostream_t;
    
    	istream_t::sentry ok_in(in);
    	ostream_t::sentry ok_out(out);
    
    	if(ok_in && ok_out)
    	{
    		ios_base::iostate state_in = ios_base::goodbit;
    		ios_base::iostate state_out = ios_base::goodbit;
    
    		try
    		{
    			const std::ctype<E_in>& ctype_in = use_facet<std::ctype<E_in>>(in.getloc());
    
    			for(;;)
    			{
    				Traits_in::int_type m = in.rdbuf()->sbumpc();
    
    				if(Traits_in::eq_int_type(m, Traits_in::eof()))
    					break;
    
    				out.rdbuf()->sputc(m);
    			}
    		}
    		catch(...) // unschön, aber ich konnte nicht feststellen, dass sbumpc() nicht throwen kann
    		{
    			state_in |= ios_base::badbit;
    			if(in.exceptions() & ios_base::badbit)
    				throw;
    
    			state_out |= ios_base::badbit;
    			if(out.exceptions() & ios_base::badbit)
    				throw;
    		}
    
    		in.setstate(state_in);
    		out.setstate(state_out);
    	}
    }
    
    // ===================================================
    
    int main()
    {
    	time_t start = time(0);
    
    	ifstream ifs("C:/SomeBigFile.pdb", ios::binary);
    	ofstream ofs("C:/SomeBigFile.pdb.copy", ios::binary);
    
    	fastCopy(ifs, ofs);
    
    	cout << "Time difference: " << difftime(time(0), start) << " seconds";
    
    	int x;
    	cin >> x;
    }
    

    Das benötigt für 79 MB im Release 3 Sekunden, mein Windows-Explorer ist nicht schneller. Natürlich ist das nur ein Performancetest, weil jetzt überhaupt noch nichts geparst oder berechnet wird (ich glaube jedoch, der Flaschenhals wird nicht die Berechnung sein, das Parsen in einen double für a muss man vermutlich schon irgendwie optimieren).

    Aber man kriegt anscheinend nicht raus, ob der throw vom istream oder ostream kam, jedenfalls kommt die exception von ios_base und hat nicht wirklich viele Informationen, also sollte man die Streams nicht mit Exceptions nutzen?! Anscheinend ist das, was ich hier gemacht habe, nicht vorgesehen. Aber spricht doch eigentlich nicht viel gegen, oder?



  • ein echter parser ist bei sowas viel viel viel schneller als iostream. Eine Größenordnung schneller sollte machbar sein.



  • War das bezugnehmend auf meinen Post? Wie soll das Ding denn schneller als das BS sein? Die Geschwindigkeit wird offensichtlich durch die Lese/Schreibgeschwindigkeit der Festplatte gedrosselt. Und wir hatten hier schon so einige Diskussionen, dass iostreams langsam sein sollen. Das stimmt aber einfach nicht, wenn man nicht get/put, operator<<, operator>>, write or read benutzt, sondern die low-level sget/sput/...-Funktionen. Dass in dem speziellen Fall Streams weniger Nutzen haben, ist natürlich ein wenig was anderes (trotzdem kann man immer noch diverse i/ostreams nutzen).

    Ich habe gerade leider keine SSD, um meine Drosselungsthese zu überprüfen...



  • Ich weiß nicht wie gut diese ganzen low-level-System-Aufrufe sind, aber vielleicht beschleunigt sich das ganze, wenn man die komplette Datei zuerst in den Speicher ließt, einen stringstream draus macht und auf dem dann arbeitet... Und das Ergebnis ebenfalls einen stringstream schreibt. Kann mir vorstellen, dass durch das einsparen der ganzen read/write-Aufrufe einiges an Performance herauszuholen ist.



  • Genau, man hantiert mit 47 x 20MB im Arbeitsspeicher herum...



  • Das macht nichtmal 1gb. Das ist heutzutage doch kein Problem mehr.



  • Na ja, streng genommen ist das doppelte, man schreibt ja auch.

    Ich finde es trotzdem egal. Ich würde nach wie vor pro Lese-Datei einen ifstream anlegen. Und meine Variante erfordert ja auch, dass man pro ifstream einen ofstream mit ios::app anlegt. Dann hat man also immer nur eine Datei auf einmal im Speicher. Das wird die Performance nicht wirklich drücken.

    Und das in einen stringstream hauen bedeutet, dass man den gesamten Kopiervorgang verdoppelt. Anstatt alles "auf dem Weg" zu lesen, kopiert man es in den Speicher und parst dann dort wiederum durch. Ich bin nicht sicher, ob man dadurch irgendwelche Page/Cache-Misses einschränkt... aber das Performance-Problem mit operator<</>> liegt einfach darin, dass für jeden Vorgang ein sentry-Objekt angelegt wird, das teure Dinge macht, siehe den von Werner verlinkten Thread, hier verlinkt mit dem Post, in dem er es begründet: http://www.c-plusplus.net/forum/p2267872#2267872

    Legt man nur zwei sentrys (pro istream/ostream) pro Datei-Parse-Vorgang an, boosted man die Performance also automatisch. Denn sputc macht außer eof-Abfrage und Schauen, ob der Buffer noch ok ist, eigentlich nichts außer das Zeichen in den Buffer zu schreiben, jedenfalls in meiner MSVC-Implementierung.

    Nächste Frage ist vermutlich, ob die Facette zum Parsen von double schnell genug ist. Falls von Werner keine grundsätzlichen Einwände kommen und OP daran Interesse hätte, weil er noch nichts Besseres fand, versuche ich das später vielleicht auch Mal. 😋



  • Hey also erstmal danke für die schöne Diskusion, blicke aber noch nicht wirklich durch, ob es jetzt schneller geht 😉

    Bzgl. SSD - habe es gerade auf SSD ausprobiert und es dauert genauso lang 😞

    Bin leider auch noch in meinen C++ Anfängen.

    Grüße

    Sorry hatte die 2. Seite nicht gesehen:

    Hierzu: Das Programm sollte unter root von Cern laufen, daher kann ich leider keine stringsteams benutzen, da das irgendwie nicht unterstützt wird. Ist es den so viel schneller als Stream? Dann würde ich halt doch ein Standalone bauen.



  • Was heißt "irgendwie nicht unterstützt"? ostream/istream funktionieren, stringstream aber nicht? sstream hast Du aber inkludiert? Was ist denn die Fehlermeldung?



  • hey,

    also das problem ist, wenn ich das program mit stringstreams ausführe in Visual Studio, funktionierte es problemlos. Habe den code dann kopiert und in der Uni mit root gestartet, und es hat nicht funktioniert. Habe es daraufhin in sprintf umgeschrieben und schon gehts. (Zuhause Windows in der Uni Linux) Fehlermeldung an weiß ich nicht. Irgendwas mit Can't open ....!



  • Can't open or load the PDB-file?



  • Hey,

    also habs gerade mal mit einem anderen Code getestet:

    Code:

    #include <fstream> 
    #include <iostream>
    #include <vector>
    // #include "H1.h"			/* wird fuer root-Histogramme benoetigt */
    // #include "TFile.h"			/* wird fuer root-Histogramme benoetigt */
    // #include <TTree.h>			/* wird fuer root-Trees benoetigt */
    //#include "TBranch.h"
    // #include "TBasket.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <iostream>
    #include <fstream>
    #include <cstdlib>
    #include <string>
    #include <math.h>
    #include <sstream>
    #include <TH2.h>
    #include <TStyle.h>
    
    // #include<TTree.h>
    
    int main()
    { 
    	double a,b,c,Channel;
    	std::stringstream filename,channelnumber;
    	filename<<"run00029_ch"; //Eintragen des Ladeverzeichnisses in der Form "%%\\run00001_ch
    	std::cout << "Welcher Channel soll geplottet werden? \n" ;
    	std::cin >> Channel;
    	channelnumber << filename.str() << Channel;
    
    	std::vector<double> x;
    	std::vector<double> y;
    	std::vector<double> t;
            std::ifstream myFile; 
            myFile.open (channelnumber.str());   
     // -----------------------root Tree erstellen
    	char rootTreeName[60];
    	char* rohdatname="Testmessung";
    
    	sprintf(rootTreeName,"%s.channel35.root",rohdatname);		// Name fuer Root Tree-Datei festlegen 
    	TFile *hfile = new TFile(rootTreeName,"RECREATE");			
    	TTree *tree = new TTree("MyTree","An example of a ROOT tree");	// Tree erstellen 
     	tree->Branch("sg", &b ,"b/D");
    	tree->Branch("lg", &c, "c/D");
    
    //------------------------- root Tree füllen
    	for (int i=1; i<1000000; i++)
    	 {
    			double a=0,b,c;
            		myFile>>a>>b>>c; 
    			x.push_back(b);
    			y.push_back(c);
    			t.push_back(a);
    			if (a<1) 
    			{break;}
    
    	tree->Fill();
    
     	} 
            myFile.close(); 
    	hfile->Write();
    	hfile->Close();
    
    }
    

    Fehler ist:
    Error: Can't call basic_ifstream<char,char_traits<char> >::open(channelnumber.str()) in current scope test.cpp:39:
    Possible candidates are...
    public: void basic_ifstream<char,char_traits<char> >::open(const char* s,ios_base::openmode mode=ios_base::in);

    Im compiler geht es aber 😞



  • Du musst zum Öffnen der Datei vor C++11 einen C-String übergeben (*.str().c_str()).
    Der andere Compiler hatte offenbar C++11, dort geht es auch mit einem std::string.



  • wie mache ich das?
    PC auf dem es geht Windows8

    wo es nicht geht:
    Ubuntu 12.10



  • Eisflamme schrieb:

    War das bezugnehmend auf meinen Post?

    Nein, aber dein code wird trotzdem lahm sein, weil er dn ganzen locale-overhad mitnimmt.

    template<typename E_in, typename Traits_in, typename E_out, typename Traits_out>
    void fastCopy(basic_istream<E_in, Traits_in>& in, basic_ostream<E_out, Traits_out>& out)
    {
        out << in.rdbuf();
    }
    

    Was ich machen würde: datei in einn string einlsen (mittels std::stringbuf) dann den string parsen und, die Werte umwandeln, wieder in einen string umwandeln und dann als block an die Ausgabedatei anhängen.



  • Okay, aber der Witz an der Sache ist dann ja: Wie parst Du den String dann?



  • Lummel202 schrieb:

    wie mache ich das?
    PC auf dem es geht Windows8

    wo es nicht geht:
    Ubuntu 12.10

    Hänge einfach ein .c_str() hinten an.


Anmelden zum Antworten