Was schnelleres als boost::lexical_cast um Zahlen und Floats zu konvertieren



  • Die Strings die ich in unsigned int (4byte) und float (4byte) konvertieren möchte habe ich in einem std::vector< unsigned char >. Ich habe auf den Anfang und auf das Ende der Zahlen einen Iterator.
    Fürs erste habe ich zum umwalden aus den Iterator-Sequenz einen temporären std::string erzeugt und diesen mit boost::lexical_cast in den Zieltyp umgewandelt.

    Allerdings ist boost::lexical_cast viel zu langsam. Laut Profiler wird in boost::lexical_cast knapp 50% der Zeit verbracht.

    atof und atoi sind nicht gerade meine Favoriten. Und da ich eh was alternatives Suche wäre es mir am liebsten, wenn ich da auch eine Iterator-Sequenz benutzen könnte.

    Kennt da jemand etwas? Hat jemand sowas schonmal implementiert (und würde mir die Arbeit ersparen :))?



  • Versuche mal das:

    #include <iostream>
    #include <streambuf>
    #include <vector>
    
    class VectorBuf : public std::streambuf
    {
    public:
        VectorBuf( const std::vector< unsigned char >& v )
        {
            if( !v.empty() )
            {
                char* p = (char*)&v[0];
                setg( p, p, p + v.size() );
            }
        }
    };
    
    int main()
    {
        using namespace std;
        vector< unsigned char > v;
        v.push_back( '1' );
        v.push_back( '.' );
        v.push_back( '5' );
    
        VectorBuf buf( v );
        istream in( &buf );
        float f;
        in >> f;
    
        return 0;
    }
    

    stehen alle Zahlen in einem vector<> oder hast Du mehrere?

    Wenn Du Wert auf die Iteratoren legst, so ginge auch noch so was

    #include <iostream>
    #include <vector>
    
    class ReadNum : public std::num_get< unsigned char, std::vector< unsigned char >::iterator >
    {};
    
    int main()
    {
        using namespace std;
        vector< unsigned char > v;
        v.push_back( '1' );
        v.push_back( '.' );
        v.push_back( '5' );
    
        float d = 0;
        basic_ios< char > ios( 0 );
        ios_base::iostate err = 0;
        ReadNum read_num;
        vector< unsigned char >::iterator i = read_num.get( v.begin(), v.end(), ios, err, d );
    
        return 0;
    }
    

    probier's mal aus und berichte uns Deine Erfahrungen.

    Gruß
    Werner



  • In dem Vektor steht eine ganze Datei, die ich parse. D.h. da stehen nicht nur Zahlen drin, aber wenn ich eine Zahl gefunden habe, dann habe ich einen Anfangs- und einen Enditerator für die Zahl.

    Die zweite Variante kommt daher in Frage, ich werde sie gleich mal testen.



  • Die zweite Variante bringt es leider ganz und gar nicht.

    Messwerte von lexical_cast:

    float: ~44.663 cycles
    dword: ~31.974 cycles

    Messwerte von std::num_get:

    float: ~52.469 cycles
    dword: ~40.691 cycles

    Da boost::lexical_cast intern einen stringstream benutzt müsste der doch intern auch mit num_get arbeiten. Daher wundert es mich, dass der direkte Einsatz langsamer ist (code wurde von unten 1:1 übernommen für float und für dword einfach statt float unsigned int genommen).

    P.S. Die Punkte sind Hundertertrenner und keine Dezimalpunkte. Nicht dass es hier zu missverständnissen kommt.

    Edit:

    Ich habe eben mal atof und atoi getestet und die Ergebnisse sind erstaunlich:

    atof: 13.886 cycles
    atoi: 1.867 cycles

    Ich musste für jeden der Aufrufe einen temporären std::string erzeugen.
    Für das Erzeugen und Zerstören hab ich leider keine Messwerte (wobei ich die beim lexical_cast auch nicht habe, da wurd auch ein temporärer String erzeugt).



  • Das atoi schnell ist, war zu vermuten. Aber du hast halt auch Komforteinbussen. Man kann nicht alles auf einmal haben, gilt hier die Devise. lexical_cast ist halt sicherer, da du z.B. eine Exception bekommst, wenn der String kein reiner Integer ist. Bei atoi bekommst du 0 zurück. Hem, war der String wirklich "0" oder ist die 0 ein Errorcode? 🙄

    Wenn dir das natürlich schnuppe ist, kannst du natürlich atoi benutzen.



  • Der String den ich an atoi/atof verfüttere ist auf jeden Fall eine gültige Zahle, d.h. als einziger Fehler könnte die Zahl außerhalb des Wertebereichs liegen.
    Da die Wertebereiche aber für das Dateiformat fest vorgegeben sind, sehe ich da jetzt nicht das Problem.

    Was passiert bei atof wenn der String einen double, also eine Zahl enthält die den Wertebereich verlässt? Bzw. gibt es einen Weg einen double in einen float zu verkleinern (auf Kosten der Genauigkeit)?



  • Zur Vollständigkeit, weil ich gerade darüber gestolpert bin:
    Im Overlaod-Magazin wird eine getunte lexical_cast-Variante vorgestellt, die um einiges schneller sein soll:
    http://www.accu.org/var/uploads/journals/overload74.pdf
    Wird wahrscheinlich auch in Boost 1.35 reinkommen.



  • lolz schrieb:

    Allerdings ist boost::lexical_cast viel zu langsam. Laut Profiler wird in boost::lexical_cast knapp 50% der Zeit verbracht.

    Die benutzen dummerweise intern die Streambuffer und haben keine selbst geschriebenen Methoden 😞



  • @Artchi: Wieso erzählst du keinem von dieser Seite? Das Journal gefällt mir ausgesprochen gut 🙂

    MfG SideWinder




Log in to reply