1,000,000 floats einlesen


  • Mod

    Habe noch ein bisschen mit dem Code rumgespielt, und eine spirit-Variante hinzugefügt (Version 4):

    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    #include <string>
    #include <vector>
    #include <ctime>
    #include <fstream>
    #include <iostream>
    
    static const char *g_fileName = "test.txt";
    static const int g_floatCount = 1000000;
    
    int main()
    {
      using namespace std;
      time_t t1;
    
      t1 = clock();
      ifstream is(g_fileName);
      cout << "Time to open: " << clock() - t1 << "ns\n";
    
      vector<float> buf;
      buf.reserve(g_floatCount);
    
      t1 = clock();
      int i = 0;
      string s;
      while ( getline( is, s ) )
      {
         namespace sp = boost::spirit;
         sp::qi::phrase_parse(s.begin(), s.end(), *sp::qi::float_ , sp::ascii::space, buf);
      };
      cout << "Time to parse: " << clock() - t1 << "ns\n";
    
      t1 = clock();
      is.close();
      cout << "Time to close: " << clock() - t1 << "ns\n";
    
      cin >> i;
      cout << buf[i] << endl;
      return 0;
    }
    

    510ms:1080ms:1830ms:570ms



  • ich hab's auch mal probiert. Meine Ergebnisse sind

    Visual Studio 2008 Express Edition, Release
    C - fread + strtod: geht gar nicht!! die Schleife mit strtod endet nicht
    C - fscanf: ~690 ms
    C++ - istream: ~1510 ms
    C++ - boost.spirit: ~790ms

    und die 5. Variante, ist wie C++ mit istream, aber eigener num_get-facette für Float
    C++ - ifstream + eigenes num_get-Float: ~610ms

    // liest float ohne(!) Vorzeichen nur in Gleitkommadarstellung (kein Exponent); keine Überlaufprüfung
    class GetFloat : public std::num_get< char >
    {
    protected:
        typedef std::istreambuf_iterator< char > iterator_type;
    
        virtual iterator_type do_get( iterator_type first, iterator_type last,
            std::ios_base& /*iosbase*/, std::ios_base::iostate& state,
            float& val ) const
        { // get float from [first, last) into val
            bool ok = false;
            val = 0.f;
            float divisor = 1.f;
            for( bool vorkomma = true; first != last; ++first )
            {
                if( *first == '.' && vorkomma )
                {
                    vorkomma = false;
                    continue;
                }
                if( *first < '0' || *first > '9' )
                    break;
                ok = true;
                (val *= 10.f) += float(*first - char('0'));
                if( !vorkomma )
                    divisor *= 10;
            }
            val /= divisor;
            if ( !ok )
                state  |= std::ios_base::failbit;
            if( first == last )
                state |= std::ios_base::eofbit;
            return first;
        }
    };
    

    Einzufügen in 'C++ - ifstream' hinter Zeile 15

    is.imbue( locale( is.getloc(), new GetFloat ) );
    

    Gruß
    Werner



  • Ich habe jetzt bisher nur mal ifstream probiert: Habe in die Datei eine Millionen floats schreiben lassen von 0.1234 bis 999999.1234. Was ich bemerkt habe ist folgendes: Wenn ich in jede Zeile, einen Wert schreibe, dann bin ich bei ~1700ms, schreibe ich dir Werte jedoch fortfolgend hintereinander, benötigt er nur ~330 ms.

    Würd jetzt ja auch noch kurz die anderen Varianten prüfen, das muss aber bis morgen warten, denn ich schreibe morgen eine Mathe-Klausur und das geht jetzt natürlich vor 😉 😃

    Lg freeG



  • Bei so komischen Werten habe ich mir mal die Mühe gemacht 3 Versionen (statisch gelinkt) zu bauen und mit den test.txt Dateien hochzuladen. (Einmal mit Zeilen, einmal mit Leerzeichen getrennt)

    http://www20.zippyshare.com/v/88004697/file.html (VS 2010)

    Es ist doch irgendwie unwahrscheinlich, dass der gleiche Code auf verschiedenen Rechnern so unterschiedlich "gut" läuft. Jetzt bin ich ja mal gespannt 😃

    (Meine Testergebnisse ändern sich kaum, sind auch mit beiden .txt Dateien fast gleich)

    Werner Salomon schrieb:

    C - fread + strtod: geht gar nicht!! die Schleife mit strtod endet nicht

    Kannst du das Problem mal näher beschreiben? Ich kann mir nicht so recht vorstellen warum die Schleife nicht enden sollte 😃

    Edit:
    Hab das Ganze auch gleich mal auf meinen Netbook getestet. (Atom 1.8GH)
    fread+strtod: ~1555ms
    fscanf: ~3200ms
    ifstream: ~4750ms

    Hat sich also nicht wirklich viel geändert..

    Edit:

    Dravere schrieb:

    C++ - ifstream: <15 ms (nicht mehr messbar)

    Bist du dir sicher, dass in deiner Datei kein #INF steht? Dann wäre die C++ Variante nämlich die einzige die abbrechen würde. <15ms scheint dann doch irgendwie unrealistisch zu sein 😃



  • So folgende Werte hab ich nun:

    test.txt

    fread-strtod: ~620ms
    fscanf : ~1300ms
    ifstream : ~1830

    test2.txt

    fread-strtod: ~620ms
    fscanf : ~1290ms
    ifstream : ~1820ms

    So nun verhält es sich bei mir zwischen beiden Text-Dateien gleich.
    Bei meiner Version war jedoch ifstream mit der Datei wie deine test2.txt wesentlich schneller. Warum weiß ich leider auch nicht.

    Lg freeG


  • 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.


Anmelden zum Antworten