1,000,000 floats einlesen


  • Mod

    Habe die spirit-Version angepasst, so dass sie wie Version1 erst einmal das ganze File einliest:

    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    #include <vector>
    #include <ctime>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    
    static const char *g_fileName = "test.txt";
    static const int g_floatCount = 1000000;
    
    int main()
    {
      using namespace std;
      time_t t = clock();
      ifstream is(g_fileName, ios_base::binary );
      is.seekg(0, std::ios_base::beg);
      ifstream::pos_type len = is.tellg();
      is.seekg(0, std::ios_base::end);
      len = is.tellg() - len;
      is.seekg(0, std::ios_base::beg);
    
      vector<char> input(len);
      is.read( &input.front(), len );
      cout << "Time to open&read: " << clock() - t << '\n';
    
      t = clock();
      vector<float> buf;
      buf.reserve(g_floatCount);
      namespace sp = boost::spirit;
      sp::qi::phrase_parse(&input[0], &input[0]+input.size(), *sp::qi::float_ , sp::ascii::space, buf);
      cout << "Time to parse: " << clock() - t << '\n';
    
      int i = 0;
      cin >> i;
      cout << buf[i] << endl;
      return 0;
    }
    

    Zeiten (jeweils lesen+parsen) für test.txt:
    Version 1 (fread-strtod) : 710 ms
    Version 2 (fscanf): 1270 ms
    Version 3 (ifstream): 1910 ms
    Version 4 (ifstream+spirit): 590 ms

    test2.txt:
    Version 1 (fread-strtod) : 690 ms
    Version 2 (fscanf): 1280 ms
    Version 3 (ifstream): 1900 ms
    Version 4 (ifstream+spirit): 580 ms



  • Möchte jemand Spirit nochmal in Verbindung mit dem Multi Pass Iterator testen?

    In Kürze: Den istream_iterator aus boost/spirit/include/support_istream_iterator.hpp benutzen (das ist ein buffernder Iterator, damit ist er ein ForwardIterator und kein InputIterator, Spirit kann somit damit arbeiten).

    Damit müsste man weder das komplette File laden, noch ein teures getline machen und hätte immer noch (denke ich) sehr gute Geschwindigkeit.



  • camper schrieb:

    Zeiten (jeweils lesen+parsen) für test.txt:
    Version 1 (fread-strtod) : 710 ms
    Version 2 (fscanf): 1270 ms
    Version 3 (ifstream): 1910 ms
    Version 4 (ifstream+spirit): 590 ms

    test2.txt:
    Version 1 (fread-strtod) : 690 ms
    Version 2 (fscanf): 1280 ms
    Version 3 (ifstream): 1900 ms
    Version 4 (ifstream+spirit): 580 ms

    Dass boost::spirit so gut abschneidet, hätte ich nie gedacht. Erstaunlich!



  • Aus Spaß an der Freude hab ich das auch mal mit C# .NET gemacht. Wenn ich keinen Perfomancefehler gemacht habe, komme ich auf 1,7 Sekunden. Vielleicht intressiert es jemanden 🤡


  • Mod

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/support_istream_iterator.hpp>
    #include <iostream>
    #include <vector>
    #include <ctime>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    
    static const char *g_fileName = "test.txt";
    static const int g_floatCount = 1000000;
    
    int main()
    {
      using namespace std;
      ifstream is(g_fileName);
      vector<float> buf;
      buf.reserve(g_floatCount);
    
      time_t t = clock();
      namespace sp = boost::spirit;
      sp::qi::phrase_parse( sp::istream_iterator(is), sp::istream_iterator(), *sp::qi::float_, sp::ascii::space, buf );
      cout << "Time to parse: " << clock() - t << '\n';
    
      int i = 0;
      cin >> i;
      cout << buf[i] << endl;
      return 0;
    }
    

    2610 ms ... allerdings ist irgendein Fehler drin, jedenfalls wird nicht alles richtig geparst. In jedem Falle ist es viel zu langsam.



  • FreakY<3Cpp schrieb:

    Aus Spaß an der Freude hab ich das auch mal mit C# .NET gemacht. Wenn ich keinen Perfomancefehler gemacht habe, komme ich auf 1,7 Sekunden. Vielleicht intressiert es jemanden 🤡

    Um die Werte zu vergleichen müsstest du jetzt schon den Code posten, sonst ist das zu CPU abhängig 😃

    So.. mit gleichen .txt's scheinen die Werte ja alle übereinzustimmen. (Nebenbei bemekert boost::spirit ist wohl echt erstaunlich gut :D)
    Bleibt die Frage, warum die C++ filestreams so viel langsamer sind? Hat jemand da eine gut Erklärung für? Dass floats da auch ein Komma als Trennzeichen haben können, scheint mir irgendwie keine ausreichende Erklärung für den immensen Zeitverlust zu sein..



  • cooky451 schrieb:

    FreakY<3Cpp schrieb:

    Aus Spaß an der Freude hab ich das auch mal mit C# .NET gemacht. Wenn ich keinen Perfomancefehler gemacht habe, komme ich auf 1,7 Sekunden. Vielleicht intressiert es jemanden 🤡

    Um die Werte zu vergleichen müsstest du jetzt schon den Code posten, sonst ist das zu CPU abhängig 😃

    static void Main(string[] args)
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        string[] array;
        using (StreamReader sr = new StreamReader("Floats.txt"))
        {
            double result;
            //floats auslesen und formatexception "abfangen"
            array = sr.ReadToEnd().Split('\n').Where(s => double.TryParse(s, out result)).ToArray();
        }
        List<float> l = new List<float>();
        //string to float konvertierung
        foreach (string s in array)
        {
            l.Add((float)Convert.ToDouble(s));
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.ReadKey();
    }
    
    /*
    für diejenigen die auf Extensions und Einzeiler stehen :P
    
    using (StreamReader sr = new StreamReader("Floats.txt"))
    {
        double result;
        List<float> list = sr.ReadToEnd().Split('\n').Where(s => double.TryParse(s, out result)).ToList()
            .ConvertAll(new Converter<string, float>(s => (float)Convert.ToDouble(s)));
    }
    
    */
    


  • Danke.
    C# braucht bei mir gerade mal ~950 ms 😮

    Edit:
    Ist also in etwa so schnell wie fscanf, hätte ich nicht gedacht.



  • cooky451 schrieb:

    Danke.
    C# braucht bei mir gerade mal ~950 ms 😮

    Edit:
    Ist also in etwa so schnell wie fscanf, hätte ich nicht gedacht.

    Ja bei mir dauerts ein bisschen länger. Hab eine etwas lahme Krücke als PC, aber ich bin zufrieden :p



  • Ich kriegs auf 375ms

    static void Main(params string[] args)
        {
    
          System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
          sw.Start();
          List<float> l = new List<float>();
          using (StreamReader sr = new StreamReader("Floats.txt"))
          {
    
            float result;
            while (!sr.EndOfStream)
            {
              if(float.TryParse(sr.ReadLine(), out result))
                l.Add(result);
            }
          }
    
          sw.Stop();
          Console.WriteLine(sw.ElapsedMilliseconds+"ms");
    
          Console.ReadKey();
    
        }
    


  • Firefighter schrieb:

    Ich kriegs auf 375ms

    Du musst natürlich auch die C++-Varianten ausführen und deren Zeiten posten. Absolut hat das sonst keinen Wert.



  • Ich bezog mich ja gerade auf die Vorherige Variante von FreakY<3Cpp



  • und wenn dein rechner jetzt einfach 5x schneller ist als seiner? deshalb auch die cpp version zum vergleich.



  • Die 2. C# Variante ist wirklich in etwa doppelt so schnell, also ~450 ms.
    (In etwa so schnell wie fread+strtod.)



  • camper schrieb:

    // Spirit-Lösung
    

    2610 ms ... allerdings ist irgendein Fehler drin, jedenfalls wird nicht alles richtig geparst. In jedem Falle ist es viel zu langsam.

    Hm, hatte auch so ähnliche Zeiten gestern Abend, kam dann aber nicht mehr dazu, weiter zu testen. Ich denke aber, es liegt daran, dass der Iterator successive die gesamte Datei puffert. Wenn du gerade Gelegenheit hast, probiere mal die Parsergrammatik *(float_ >> boost::spirit::repository::flush_multi_pass) . Damit wird nach jedem eingelesenen float der Buffer gelöscht. Das flush_multi_pass findet man in boost/spirit/repository/include/qi_flush_multi_pass.hpp .
    Schneller, als vorher die gesamte Datei in einen vector zu laden, wird es aber vermutlich nicht sein.



  • cooky451 schrieb:

    Die 2. C# Variante ist wirklich in etwa doppelt so schnell, also ~450 ms.
    (In etwa so schnell wie fread+strtod.)

    Also nehmt euch ein Beispiel am 2. Quellcode 🤡


  • Mod

    ipsec schrieb:

    camper schrieb:

    // Spirit-Lösung
    

    2610 ms ... allerdings ist irgendein Fehler drin, jedenfalls wird nicht alles richtig geparst. In jedem Falle ist es viel zu langsam.

    Hm, hatte auch so ähnliche Zeiten gestern Abend, kam dann aber nicht mehr dazu, weiter zu testen. Ich denke aber, es liegt daran, dass der Iterator successive die gesamte Datei puffert. Wenn du gerade Gelegenheit hast, probiere mal die Parsergrammatik *(float_ >> boost::spirit::repository::flush_multi_pass) . Damit wird nach jedem eingelesenen float der Buffer gelöscht. Das flush_multi_pass findet man in boost/spirit/repository/include/qi_flush_multi_pass.hpp .
    Schneller, als vorher die gesamte Datei in einen vector zu laden, wird es aber vermutlich nicht sein.

    2630 ms, also keine Verbesserung. Der Bug ist immer noch drin, die ersten 4542 Zahlen korrekt eingelesen, danach gibt es Abweichungen. Allerdings habe ich nicht die Zeit zum debuggen. Der Programmcode ist ja simpel genug, also vermutlich korrekt. Bleibt die Möglichkeit, dass die Biblithek ein Problem hat oder der Compiler fehlerhaften Code erzeugt.



  • #include <ctime> 
    #include <fstream> 
    #include <iostream> 
    
    int main() 
    { 
    	std::ifstream input(".\\test.txt");
    
    	// create copy of current global locale
    	std::locale loc;
    
    	// iterator to read from standard input
    	typedef std::istreambuf_iterator<char> InIt;
    	InIt beg = InIt(input);
    	InIt end = InIt();
    
    	// stream which defines input format
    	std::ios_base& fmt = input;
    
    	// declare output arguments
    	std::ios_base::iostate err = std::ios::goodbit;
    
    	// get numeric input facet of the locale loc
    	std::num_get<char, InIt> const& ng
    		 = std::use_facet<std::num_get<char, InIt> >(loc);
    
    	float* data = new float[1000000]; 
    	clock_t startClock = std::clock(); 
    
    	for(int i = 0; (i < 1000000) && (err == std::ios::goodbit); ++i) 
    	{ 
      		ng.get(beg, end, fmt, err, data[i]);
      		++beg;
    	} 
    
    	clock_t endClock = std::clock(); 
    
    	double duration = endClock - startClock; 
    	duration /= CLOCKS_PER_SEC; 
    
    	std::cout << duration << std::endl; 
    
    	delete[] data; 
    
    	return 0; 
    }
    

    strtod / fscanf / stream / streambuffer ~ 0.47 / 1 / 1.4 / 0.94

    Anregung aus
    http://www.josuttis.com/libbook/i18n/numget.cpp.html

    Gruß


Anmelden zum Antworten