anfängerprobleme mit sprintf



  • Swordfish schrieb:

    Tachyon schrieb:

    Sorry, aber Hackers Lösung ist deutlich besser.

    Mhm. Erklärst Du mir auch, warum?

    Weil Deine Version sich sinnfreierweise so ziemlich alle Probleme einfängt, die static-Variablen so mit sich bringen. Was versprichst Du Dir davon? Ein Performancegewinn?



  • Tachyon schrieb:

    Ethon schrieb:

    Oder einfach C-API. Da spart man sich den ganzen Streamoverhead (mind. 1 zusätzliche Allokation).

    std::string to_hex_string(unsigned value)
    {
        char buffer[std::numeric_limits<unsigned>::digits];
        return std::string(std::itoa(value, buffer, 16));
    }
    

    Nur, dass itoa kein Standard-C ist.

    Imho fast egal, da ich noch nie einen Compiler gesehen habe, der es nicht kann. Na dann einfach die Idee des OPs gefixt:

    std::string to_hex_string(unsigned value)
    {
        char buffer[sizeof(value) * 2 + 1];
        sprintf(buffer,"%x", value); 
        return std::string(buffer);
    }
    

    Immer noch schneller als per stringstream.



  • Tachyon schrieb:

    Weil Deine Version sich sinnfreierweise so ziemlich alle Probleme einfängt, die static-Variablen so mit sich bringen.

    Im konkreten Fall? Sorry, ich wills wirklich wissen.

    Tachyon schrieb:

    Was versprichst Du Dir davon? Ein Performancegewinn?

    Ja, wenn ich auch nicht gemessen habe.



  • Swordfish schrieb:

    Tachyon schrieb:

    Weil Deine Version sich sinnfreierweise so ziemlich alle Probleme einfängt, die static-Variablen so mit sich bringen.

    Im konkreten Fall? Sorry, ich wills wirklich wissen.

    Was wenn bspw. Fehlerbits gesetzt werden?



  • Es ist auf jeden Fall nicht Threadsafe. Mit C++11 und thread_local könnte es Sinn machen, aber Variable reusing neigt oft dazu die Performance zu drücken, da der Compiler schlechter optimieren kann.



  • Swordfish schrieb:

    Tachyon schrieb:

    Weil Deine Version sich sinnfreierweise so ziemlich alle Probleme einfängt, die static-Variablen so mit sich bringen.

    Im konkreten Fall? Sorry, ich wills wirklich wissen.

    Ok, Threadsafe ist es (noch) nicht. Sonst?

    Swordfish schrieb:

    Tachyon schrieb:

    Was versprichst Du Dir davon? Ein Performancegewinn?

    Ja, wenn ich auch nicht gemessen habe.

    Gemessen: // edit Fehlerbits noch löschen...

    Swordfish: 591.937
    Hacker:    1005.78
    Ethon:     171.649
    

    Gut, daß uns sprintf schlägt, ist klar...

    #include <iostream>
    #include <sstream>
    #include <iomanip>
    #include <string>
    #include <cstdlib>
    
    #include <Windows.h>
    #define WIN32_LEAN_AND_MEAN
    
    std::string to_hex_string_swordfish( unsigned value ) {
    	static std::ostringstream ss;
    	ss.clear( );
    	ss.str( "" );
    	ss << std::hex << value;
    	return ss.str( );
    }
    
    std::string to_hex_string_hacker( unsigned value)
    {
        std::ostringstream stream;
        stream << std::hex << value;
        return stream.str();
    } 
    
    std::string to_hex_string_ethon(unsigned value)
    {
        char buffer[sizeof(value) * 2 + 1];
        sprintf(buffer,"%x", value);
        return std::string(buffer);
    }
    
    int main( )
    {
    	HANDLE me = GetCurrentProcess( );
    	if( !SetPriorityClass( me, REALTIME_PRIORITY_CLASS ) ) {
    		std::cerr << "SetPriorityClass failed!\n";
    		return EXIT_FAILURE;
    	}
    
    	std::srand( static_cast< unsigned >( std::time( 0 ) ) );
    
        LARGE_INTEGER start, end;
    
    	if( !QueryPerformanceFrequency( &start) ) {
            std::cerr << "QueryPerformanceFrequency failed!\n";
    		return EXIT_FAILURE;
    	}
    
        double frequency = start.QuadPart / 1000.0;
    	double elapsed;
    
    	std::string result;
    
    	// Swordfish
    	QueryPerformanceCounter( &start );
    
    	for( size_t i = 0; i < 1000000; ++i ) {
    
    		result = to_hex_string_swordfish( rand( ) );
    	}
    
        QueryPerformanceCounter( &end );
    	elapsed = ( end.QuadPart - start.QuadPart ) / frequency;
    
    	std::cout << "Swordfish: " << elapsed << std::endl;
    
    	// Hacker
    	QueryPerformanceCounter( &start );
    
    	for( size_t i = 0; i < 1000000; ++i ) {
    
    		result = to_hex_string_hacker( rand( ) );
    	}
    
        QueryPerformanceCounter( &end );
    	elapsed = ( end.QuadPart - start.QuadPart ) / frequency;
    
    	std::cout << "Hacker:    " << elapsed << std::endl;
    
    	// Ethon
    	QueryPerformanceCounter( &start );
    
    	for( size_t i = 0; i < 1000000; ++i ) {
    
    		result = to_hex_string_ethon( rand( ) );
    	}
    
        QueryPerformanceCounter( &end );
    	elapsed = ( end.QuadPart - start.QuadPart ) / frequency;
    
    	std::cout << "Ethon:     " << elapsed << std::endl;
    }
    

    🙄


  • Mod

    Hat zwar nichts mit dem Thema zu tun... aber

    #define WIN32_LEAN_AND_MEAN
    

    nachdem du <windows.h> bereits eingebunden hast, scheint mir sinnlos zu sein.



  • Teste doch nochmal das hier:

    template<typename CharT>
        std::basic_string<CharT> to_hex_string(unsigned value)
        {
            typedef std::basic_string<CharT> string_type;
            string_type result(sizeof(value) * 2, 0);
            typename string_type::reverse_iterator iter = result.rbegin();
            while(value > 16)
            {
                unsigned cur = value % 16;
                *iter = cur < 10 ? CharT('0') + cur : CharT('A') + cur - 10;
                ++iter;
                value /= 16;
            }
    
            *iter = value < 10 ? CharT('0') + value : CharT('A') + value - 10;
            return result;
        }
    


  • camper schrieb:

    Hat zwar nichts mit dem Thema zu tun... aber

    #define WIN32_LEAN_AND_MEAN
    

    nachdem du <windows.h> bereits eingebunden hast, scheint mir sinnlos zu sein.

    Äh, ja 😉

    Ethon schrieb:

    Teste doch nochmal das hier:

    Ethon:    33.6387
    


  • Danke, wundert mich gerade etwas. Hat meine Implementierung irgendeine Macke? Denn std::to_string (C++11) schlage ich um mehr als das Doppelte.

    So siehts jetzt aus:

    template<typename CharT, int Base>
        std::basic_string<CharT> to_string(unsigned value)
        {
            CharT const lookup[] =
                {   CharT('0'), CharT('1'), CharT('2'), CharT('3'), CharT('4'),
                    CharT('5'), CharT('6'), CharT('7'), CharT('8'), CharT('9'),
                    CharT('A'), CharT('B'), CharT('C'), CharT('D'), CharT('E'),
                    CharT('F') };
    
            typedef std::basic_string<CharT> string_type;
            string_type result(sizeof(value) * 2, 0);
            typename string_type::reverse_iterator iter = result.rbegin();
            while(value > Base)
            {
                *iter = lookup[value % Base];
                ++iter;
                value /= Base;
            }
    
            *iter = lookup[value];
            return result;
        }
    

    Kompilieren tu ich das Ganze mit dem GCC und -O3.

    Edit: Ahhh, ich seh grad, wenn die Zahlen zu klein sind funktionierts nicht. Hmm ...



  • Tja, Ethon, du bist ein Genius. 😃



  • Hacker schrieb:

    Tja, Ethon, du bist ein Genius. 😃

    Lässt sich doch fixen. Bei 2x Speedup sollte das danach immer noch deutlich schneller sein. Mal ausprobieren.



  • Ethon schrieb:

    Hacker schrieb:

    Tja, Ethon, du bist ein Genius. 😃

    Lässt sich doch fixen. Bei 2x Speedup sollte das danach immer noch deutlich schneller sein. Mal ausprobieren.

    Denkst du, dass, wenn du's an ISO/IEC schickst, sie es dann in den nächsten aufnehmen? 😃



  • template<typename CharT, unsigned Base>
        std::basic_string<CharT> to_string(unsigned value)
        {
            static_assert(Base <= 16, "Unsupported Base");
    
            CharT const lookup[] =
            {
                CharT('0'), CharT('1'), CharT('2'), CharT('3'),
                CharT('4'), CharT('5'), CharT('6'), CharT('7'),
                CharT('8'), CharT('9'), CharT('A'), CharT('B'),
                CharT('C'), CharT('D'), CharT('E'), CharT('F')
            };
    
            std::array<CharT, std::numeric_limits<unsigned>::digits> buffer;
            auto iter = buffer.rbegin();
            while(value > Base)
            {
                *iter = lookup[value % Base];
                ++iter;
                value /= Base;
            }
    
            *iter = lookup[value];
            return std::basic_string<CharT>(&(*iter), buffer.end());
        }
    

    Gefixt und noch schneller geworden.

    Zum Konvertieren von 10.000.000 rand Werten braucht meine Funktion

    real 0m11.909s
    user 0m11.905s
    sys 0m0.000s

    und std::to_string

    real 0m26.853s
    user 0m26.838s
    sys 0m0.004s

    Also, was ist der Unterschied?



  • Ethon!! Welchen Compiler benutzt du? Alter, mein GCC 4.7 meckert, dass to_string nicht deklariert ist (obwohl ich string implementiere). Was soll ich nur tun :schluchz:



  • Gcc 4.6.1

    Kompiliert mit:

    g++ -o test -O3 -march=native -std=c++0x test.cpp

    Für std::to_string brauchst du <string> und den C++11 Switch.



  • Ethon schrieb:

    Gcc 4.6.1

    Kompiliert mit:

    g++ -o test -O3 -march=native -std=c++0x test.cpp

    Für std::to_string brauchst du <string> und den C++11 Switch.

    Funktioniert immer noch nicht. 😞



  • Ethon schrieb:

    return std::basic_string<CharT>(&(*iter), buffer.end());
    

    Bist Du sicher, daß basic_string diesen c-tor haben muss?



  • [quote="Swordfish"]

    Ethon schrieb:

    return std::basic_string<CharT>(&(*iter), buffer.end());
    

    Bist Du sicher, daß basic_string diesen c-tor haben muss?

    Jeder Container lässt sich garantiert mit einem Inputiteratorenpaar konstruieren, bin mir zu 100% sicher dass es auch für std::basic_string vorgeschrieben ist, man findet ihn auch in jeder C++ Referenz.



  • Ich hab auch noch eine Idee:

    #include <algorithm>
    
    std::string to_hex_string(unsigned value)
    {
        static std::string hex;
        hex.clear();
        do
        {
            if(value%16 >= 0 && value%16 < 10)
                hex += value%16 + '0';
            else if(value%16 > 9 && value%16 < 17)
                hex += value%16 + 55;
        } while(value /= 16);
        std::reverse(hex.begin(), hex.end());
        return hex;
    }
    

    Swordfish: 414.683
    Hacker: 1317.21
    Meine: 284.66

    Außerdem braucht bei mir

    std::string to_hex_string(unsigned value)
    {
        char buffer[sizeof(value) * 2 + 1];
        sprintf(buffer,"%x", value);
        return std::string(buffer);
    }
    

    sogar mit 2370.08 deutlich länger (mit -O3)...


Anmelden zum Antworten