Benutzen von 'rdbuf()'



  • Du musst das relativ sehen. Caching, Festplattengeschwindigkeit etc machen einen großen Unterschied.



  • Was passiert wenn man Variante 6 als erstes testet? Caching könnte die Ergebnisse verfälschen.



  • Jep. Eigentlich müsste mn ja mit nem strinngstream testen.



  • Ethon schrieb:

    Du musst das relativ sehen. Caching, Festplattengeschwindigkeit etc machen einen großen Unterschied.

    Caching von was? Hat meine Festplatte ein besseres Caching oder Linux? Genau das ist ja die Frage.

    Ich verweise ja auf den Beitrag von Swordfish, der meint, sein System sei keine lahme Krücke. Da frage ich mich halt, ob meine Festplatte so gigantisch gut ist oder mein Betriebssystem.

    Und noch zu der Frage von Oberon_O: Hier sind die Ergebnisse, wenn ich Test 6 an den Anfang verschiebe:
    elapsed_variante_1 = 4.045
    elapsed_variante_2 = 1.559
    elapsed_variante_3 = 2.699
    elapsed_variante_4 = 0.091
    elapsed_variante_5 = 0.09
    elapsed_variante_6 = 0.081

    Also nein - es macht nichts aus.



  • Caching von was? Hat meine Festplatte ein besseres Caching oder Linux? Genau das ist ja die Frage.

    Afaik cacht Liux beträchtlich mehr. Bei Linux beschwert sich ja niemand wenn das OS nach dem Start mal eben 4GB RAM verwendet.



  • Ethon schrieb:

    Jep. Eigentlich müsste mn ja mit nem strinngstream testen.

    Hier meine Ergebnisse mit ostringstream:

    elapsed_variante_1 = 3.498
    elapsed_variante_2 = 1.545
    elapsed_variante_3 = 2.407
    elapsed_variante_4 = 0.077
    elapsed_variante_5 = 0.077
    elapsed_variante_6 = 0.075



  • Ethon schrieb:

    Jep. Eigentlich müsste mn ja mit nem strinngstream testen.

    Eigentlich nicht. Es geht ja um das blockweise kopieren.



  • stirni schrieb:

    Ethon schrieb:

    Jep. Eigentlich müsste mn ja mit nem strinngstream testen.

    Eigentlich nicht. Es geht ja um das blockweise kopieren.

    Höh? Es geht doch um die Performance der verschiedenen Methoden um Daten von Stream a nach Stream b zu kopieren.



  • Ethon schrieb:

    stirni schrieb:

    Ethon schrieb:

    Jep. Eigentlich müsste mn ja mit nem strinngstream testen.

    Eigentlich nicht. Es geht ja um das blockweise kopieren.

    Höh? Es geht doch um die Performance der verschiedenen Methoden um Daten von Stream a nach Stream b zu kopieren.

    Ach ich habe eher das Gefühl, wir probieren mal dies und mal das. Und ich finde das sehr spannend 👍 .

    Wenn es nur um das kopieren eines Streams in einen anderen ginge, sollten wir eher mal einfach von einem std::istringstream in einen std::ostringstream kopieren. Sollten wir das mal ausprobieren?



  • tntnet schrieb:

    Wenn es nur um das kopieren eines Streams in einen anderen ginge, sollten wir eher mal einfach von einem std::istringstream in einen std::ostringstream kopieren. Sollten wir das mal ausprobieren?

    Klar wieso nicht. 🙂 Ich werde es dann auch noch auf meinem Heimrechner testen.



  • tntnet schrieb:

    Ich verweise ja auf den Beitrag von Swordfish, der meint, sein System sei keine lahme Krücke. [...]

    Ist es auch nicht. Bei den Platten handelt es sich um 3 Seagate Constellation an einem Areca 6 GB/s SAS Raid Controller im RAID 5. Ich fürchte eher, daß der QPI-Link mit nur ~3,3 GHz (zwecks OC'ing) der Flaschenhals ist. Ich teste später nochmal mit QPI @ 6,4 GHz.



  • Alter. Hör mal auf mit deinem System anzugeben.



  • beneider schrieb:

    Alter. Hör mal auf mit deinem System anzugeben.

    Wieso sollte man darauf neidisch sein? 🙄 Edit: Neh, Spaß beiseite. Das ist schon geil.



  • Ich habe jetzt mal mit stringstream getestet. Hier sind die Ergebnisse:
    elapsed_variante_1 = 3.51375
    elapsed_variante_2 = 1.53196
    elapsed_variante_3 = 2.4565
    elapsed_variante_4 = 0.0676401
    elapsed_variante_5 = 0.0679622
    elapsed_variante_6 = 0.0699324

    Was wir hier sehen ist, dass der ostream_iterator langsamer ist, als der istream_iterator. Das ist auch mal eine interessante Erkenntnis.

    Ich habe übrigens das Testprogramm ein wenig überarbeitet. Diese grauenvollen Pointer-casts habe ich ersetzt. Und Systemspezifische Sachen gehören abstrahiert. Statt Windows Interfaces für Linux zu implementieren habe ich ein einheitliches Interface für beide gemacht.

    Ich habe das nur auf Linux getestet, da ich kein Windows habe. Dennoch habe ich versucht, die WIN32-Version auch zu implementieren. Könnt ihr mal testen?

    Hier ist mein Code:

    #include <iostream>
    #include <iterator>
    #include <sstream>
    #include <algorithm>
    #include <time.h>
    #ifdef _WIN32
    #    include <Windows.h>
    #else
    #    include <sys/time.h>
    #    include <unistd.h>
    #endif
    
    #ifdef _WIN32
    class Clock
    {
        static LARGE_INTEGER _frequency;
        LARGE_INTEGER _start;
    
      public:
        Clock();
        void reset();
        double elapsed() const;
    };
    
    LARGE_INTEGER Clock::_frequency = 0;
    
    Clock::Clock()
    {
        if (_frequency == 0)
            QueryPerformanceFrequency(&_frequency);
        reset();
    }
    
    void Clock::reset()
    {
        QueryPerformanceCounter(_start);
    }
    
    double Clock::elapsed() const
    {
        LARGE_INTEGER end;
        QueryPerformanceCounter(end);
        return static_cast<double>(end - _start) / _frequency;
    }
    
    #else
    
    class Clock
    {
        struct timespec _start;
    
      public:
        Clock();
        void reset();
        double elapsed() const;
    };
    
    Clock::Clock()
    {
        reset();
    }
    
    void Clock::reset()
    {
        clock_gettime(CLOCK_MONOTONIC, &_start);
    }
    
    double Clock::elapsed() const
    {
        struct timespec end;
        clock_gettime(CLOCK_MONOTONIC, &end);
        return (end.tv_sec - _start.tv_sec - 1) + (1000000000 + end.tv_nsec - _start.tv_nsec) / 1e9;
    }
    
    #endif
    
    int main(int argc, char* argv[])
    {
        std::istringstream inp(std::string(104857600, ' '));
    
        // Variante 1: std::istream_iterator + std::ostream_iterator
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            std::copy( std::istream_iterator<char>(inp>>std::noskipws), (std::istream_iterator<char>()), std::ostream_iterator<char>(out) );
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_1 = " << elapsed << std::endl;
        }
    
        // Variante 2: std::istream_iterator + std::ostreambuf_iterator
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            std::copy( std::istream_iterator<char>(inp>>std::noskipws), (std::istream_iterator<char>()), std::ostreambuf_iterator<char>(out) );
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_2 = " << elapsed << std::endl;
        }
    
        // Variante 3: std::istreambuf_iterator + std::ostream_iterator
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            std::copy( std::istreambuf_iterator<char>(inp), std::istreambuf_iterator<char>(), std::ostream_iterator<char>(out) );
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_3 = " << elapsed << std::endl;
        }
    
        // Variante 4: std::istreambuf_iterator + std::ostreambuf_iterator
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            std::copy( std::istreambuf_iterator<char>(inp), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(out) );
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_4 = " << elapsed << std::endl;
        }
    
        // Variante 5: einfach rdbuf
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            out << inp.rdbuf();
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_5 = " << elapsed << std::endl;
        }
    
        // Variante 6: read + write (8192 Byte Blöcke)
        {
            std::ostringstream out;
            inp.clear();
            inp.seekg(0);
    
            Clock clock;
    
            char buffer[8192];
            while( inp.read(buffer, sizeof(buffer)) && inp.gcount()>0 )
            {
                out.write(buffer, inp.gcount());
            }
    
            double elapsed = clock.elapsed();
            std::cout << "elapsed_variante_6 = " << elapsed << std::endl;
        }
    
    }
    


  • Ich krieg folgendes:

    elapsed_variante_1 = 4.82
    elapsed_variante_2 = 2.121
    elapsed_variante_3 = 3.494
    elapsed_variante_4 = 0.281
    elapsed_variante_5 = 0.265
    elapsed_variante_6 = 0.296
    

    Hab aber meine eigene Timerklasse benutzt, ich krieg bei deinem Code Fehler die ich nicht nach 10 Sekunden beheben konnte:

    main.cpp|25|Fehler: Umwandlung von »int« in nicht-skalaren Typen »LARGE_INTEGER {aka _LARGE_INTEGER}« angefordert|
    main.cpp|29|Fehler: keine Übereinstimmung für »operator==« in »Clock::_frequency == 0«|
    main.cpp|29|Anmerkung: Kandidat ist:|
    c:\mingw\bin\..\lib\gcc\mingw32\4.6.2\..\..\..\..\include\objbase.h|78|Anmerkung: BOOL operator==(const GUID&, const GUID&)|
    c:\mingw\bin\..\lib\gcc\mingw32\4.6.2\..\..\..\..\include\objbase.h|78|Anmerkung:   keine bekannte Umwandlung für Argument 1
     von »LARGE_INTEGER {aka _LARGE_INTEGER}« nach »const GUID& {aka const _GUID&}«|
    main.cpp|36|Fehler: »LARGE_INTEGER {aka _LARGE_INTEGER}« kann nicht nach »PLARGE_INTEGER {aka _LARGE_INTEGER*}« für Argument »1«
     nach »BOOL QueryPerformanceCounter(PLARGE_INTEGER)« umgewandelt werden|
    main.cpp|42|Fehler: »LARGE_INTEGER {aka _LARGE_INTEGER}« kann nicht nach »PLARGE_INTEGER {aka _LARGE_INTEGER*}« für Argument »1«
     nach »BOOL QueryPerformanceCounter(PLARGE_INTEGER)« umgewandelt werden|
    main.cpp|43|Fehler: keine Übereinstimmung für »operator-« in »end - ((const Clock*)this)->Clock::_start«|
    ||=== Build finished: 8 errors, 0 warnings ===|
    


  • Ach ja. Also erst mal erwartet QueryPerformanceCounter offensichtlich einen Zeiger. Also muss es

    QueryPerformanceCounter(&end);
    

    bzw.

    QueryPerformanceCounter(&_start);
    

    heißen.

    Wie man jetzt einen LARGE_INTEGER korrekt initalisiert, weiß ich nicht. Ist schon ein merkwürdiger Datentyp, wenn ich ihn nicht mit 0 initialisieren kann. Wie wäre es mit:

    LARGE_INTEGER Clock::_frequency = static_cast<LARGE_INTEGER>(0);
    

    Wie gesagt, habe ich kein Windows. Daher war das ein Blindflug.



  • LARGE_INTEGER Clock::_frequency = static_cast<LARGE_INTEGER>(0);
    

    Hatte ich auch probiert, klappt nicht.
    Witzig, ich hatte genau dasselbe probiert was du da gerade ansprichst.



  • Incocnito schrieb:

    LARGE_INTEGER Clock::_frequency = static_cast<LARGE_INTEGER>(0);
    

    Hatte ich auch probiert, klappt nicht.
    Witzig, ich hatte genau dasselbe probiert was du da gerade ansprichst.

    LARGE_INTEGER ist ein union oder struct .

    #if defined(MIDL_PASS)
    typedef struct _LARGE_INTEGER {
    #else // MIDL_PASS
    typedef union _LARGE_INTEGER {
        struct {
            DWORD LowPart;
            LONG HighPart;
        } DUMMYSTRUCTNAME;
        struct {
            DWORD LowPart;
            LONG HighPart;
        } u;
    #endif //MIDL_PASS
        LONGLONG QuadPart;
    } LARGE_INTEGER;
    

    Läuft auf Windows und liefert bessere Ausgaben:

    #include <iostream>
    #include <iterator>
    #include <sstream>
    #include <algorithm>
    #include <vector>
    
    #ifdef _WIN32
    #	include <Windows.h>
    #else
    #	include <sys/time.h>
    #	include <unistd.h>
    #endif
    
    namespace
    {
    #ifdef _WIN32
    	class Clock
    	{
    		LARGE_INTEGER _frequency;
    		LARGE_INTEGER _start;
    
    	public:
    		Clock();
    		void reset();
    		double elapsed() const;
    	};
    
    	Clock::Clock()
    	{
    		QueryPerformanceFrequency(&_frequency);
    		reset();
    	}
    
    	void Clock::reset()
    	{
    		QueryPerformanceCounter(&_start);
    	}
    
    	double Clock::elapsed() const
    	{
    		LARGE_INTEGER end;
    		QueryPerformanceCounter(&end);
    		return static_cast<double>(end.QuadPart - _start.QuadPart) / _frequency.QuadPart;
    	}
    
    #else
    
    	class Clock
    	{
    		struct timespec _start;
    
    	public:
    		Clock();
    		void reset();
    		double elapsed() const;
    	};
    
    	Clock::Clock()
    	{
    		reset();
    	}
    
    	void Clock::reset()
    	{
    		clock_gettime(CLOCK_MONOTONIC, &_start);
    	}
    
    	double Clock::elapsed() const
    	{
    		struct timespec end;
    		clock_gettime(CLOCK_MONOTONIC, &end);
    		return (end.tv_sec - _start.tv_sec - 1) + (1000000000 + end.tv_nsec - _start.tv_nsec) / 1e9;
    	}
    #endif
    
    	template <class Name, class Case>
    	void benchmarkCopy(const Name &name, std::istream &source, const Case &c)
    	{
    		source.clear();
    		source.seekg(0, std::ios::beg);
    		std::ostringstream sink;
    		Clock clock;
    		c(static_cast<std::ostream &>(sink));
    		const auto elapsed = clock.elapsed();
    		std::cout << name << ": " << elapsed << "\n";
    	}
    
    	void runBenchmarks(std::istream &source)
    	{
    		benchmarkCopy("std::istream_iterator + std::ostream_iterator",
    			source,
    			[&source](std::ostream &sink)
    		{
    			std::copy( std::istream_iterator<char>(source>>std::noskipws), (std::istream_iterator<char>()), std::ostream_iterator<char>(sink) );
    		});
    
    		benchmarkCopy("std::istream_iterator + std::ostreambuf_iterator",
    			source,
    			[&source](std::ostream &sink)
    		{
    			std::copy( std::istream_iterator<char>(source>>std::noskipws), (std::istream_iterator<char>()), std::ostreambuf_iterator<char>(sink) );
    		});
    
    		benchmarkCopy("std::istreambuf_iterator + std::ostream_iterator",
    			source,
    			[&source](std::ostream &sink)
    		{
    			std::copy( std::istreambuf_iterator<char>(source>>std::noskipws), (std::istreambuf_iterator<char>()), std::ostream_iterator<char>(sink) );
    		});
    
    		benchmarkCopy("std::istreambuf_iterator + std::ostreambuf_iterator",
    			source,
    			[&source](std::ostream &sink)
    		{
    			std::copy( std::istreambuf_iterator<char>(source>>std::noskipws), (std::istreambuf_iterator<char>()), std::ostreambuf_iterator<char>(sink) );
    		});
    
    		benchmarkCopy("rdbuf",
    			source,
    			[&source](std::ostream &sink)
    		{
    			sink << source.rdbuf();
    		});
    
    		{
    			const auto benchmarkCopyWithBuffer = [&](std::size_t bufferSize)
    			{
    				auto &source_ = source;
    				benchmarkCopy("read + write (" + std::to_string(static_cast<unsigned long long>(bufferSize)) + ")",
    					source,
    					[bufferSize, &source_](std::ostream &sink)
    				{
    					std::vector<char> buffer(bufferSize);
    
    					while (source_.read(buffer.data(), bufferSize) &&
    						(source_.gcount() > 0))
    					{
    						sink.write(buffer.data(), source_.gcount());
    					}
    				});
    			};
    
    			benchmarkCopyWithBuffer(1UL << 12);
    			benchmarkCopyWithBuffer(1UL << 13);
    			benchmarkCopyWithBuffer(1UL << 14);
    		}
    	}
    }
    
    int main(int argc, char* argv[])
    {
    	std::istringstream source(std::string(104857600, ' '));
    	runBenchmarks(source);
    }
    
    std::istream_iterator + std::ostream_iterator: 27.712
    std::istream_iterator + std::ostreambuf_iterator: 12.7851
    std::istreambuf_iterator + std::ostream_iterator: 17.8878
    std::istreambuf_iterator + std::ostreambuf_iterator: 2.7349
    rdbuf: 2.09577
    read + write (4096): 0.568411
    read + write (8192): 0.520136
    read + write (16384): 0.521555
    


  • TyRoXx schrieb:

    LARGE_INTEGER ist ein union oder struct .

    #if defined(MIDL_PASS)
    typedef struct _LARGE_INTEGER {
    #else // MIDL_PASS
    typedef union _LARGE_INTEGER {
        struct {
            DWORD LowPart;
            LONG HighPart;
        } DUMMYSTRUCTNAME;
        struct {
            DWORD LowPart;
            LONG HighPart;
        } u;
    #endif //MIDL_PASS
        LONGLONG QuadPart;
    } LARGE_INTEGER;
    

    Da passt ja Deine Signatur: "Faustregel zu union: Du verwendest es falsch!" 😉 . Unions sind halt ein merkwürdiges Konstrukt und sollten vermieden werden.



  • GhostfaceChilla schrieb:

    Kann mir jemand mal erklären wie ich es benutzen und auf was man acht geben muss?

    s. Kapitel 6

    http://magazin.c-plusplus.net/artikel/Ein- und Ausgabe in CPlusPlus - IO-Streams

    rdbuf() ist eine Methode(von jedem Stream), die einen Zeiger auf den dazugehörigen Stream zurückgibt. So kann man den Puffer mit der Ausgabe auf der Konsole koppeln.

    Ein kleines Beispiel hatte seldon ja schon geschrieben.


Anmelden zum Antworten