\n vs endl in cout



  • In disem (relativ alten) Artikel über \n vs endl gibt es am Ende einen interessanten Twist:

    http://www.aristeia.com/Papers/C++ReportColumns/novdec95.pdf

    "My guess is that cout is still buffered under many (if not most)
    implementations, but the standard will be the standard, so in the fu-
    ture, cout will not be buffered. Yet most of the places where I ob-
    serve endl being used gratuitously, the destination stream is cout.
    Under those conditions, the call to endl is still unnecessary (the
    stream is already being flushed after each write), but it’s no longer
    defeating the buffering system"

    ist das heute noch wahr? Wenn ja, dann könnten wir in diesem FOrum ein und für alle mal beshcließen, dass cout<<x<<endl; nicht böse ist.



  • Ein Zitat aus dem Standard (damals Draft) wäre nicht schlecht gewesen. Ich kann das nämlich nicht direkt nachvollziehen. Nur bei (w)cerr steht dabei, dass unitbuf ungleich 0 initialisiert wird.



  • endl ist immer Boese, weil cin zu cout getied ist. Flushen ist daher nie noetig



  • Mal sehen, ob das einen Unterschied macht.

    EDIT Änderungen:
    - steady_clock statt monotonic_clock für gültiges C++11
    - aufwärmen, um Caching-Effekte zu vermeiden

    #include <iostream>
    #include <chrono>
    
    template <class Action>
    static std::chrono::steady_clock::duration measure_n_times(long long n, Action const &action)
    {
      auto start = std::chrono::steady_clock::now();
      for (long long i = 0; i < n; ++i)
      {
        action();
      }
      auto finish = std::chrono::steady_clock::now();
      return (finish - start);
    }
    
    int main()
    {
      auto n = 10000;
    
      //aufwärmen
      measure_n_times(n, []
      {
        std::cout << "Hello, world!\n";
        std::cout << "Hello, world!" << std::endl;
      });
    
      auto nl = measure_n_times(n, [] { std::cout << "Hello, world!\n"; });
      std::cout.flush();
    
      auto endl = measure_n_times(n, [] { std::cout << "Hello, world!" << std::endl; });
    
      std::cerr << "nl:   " << nl.count() << '\n';
      std::cerr << "endl: " << endl.count() << '\n';
      std::cerr << "nl/endl: " << (static_cast<double>(nl.count()) / static_cast<double>(endl.count())) << '\n';
      std::cin.get();
    }
    

    Windows 7, Visual C++ 2013 schrieb:

    nl: 57408000
    endl: 56628000
    nl/endl: 1.01377

    std::endl ist auf diesem System anscheinend minimal schneller.



  • TyRoXx schrieb:

    Windows 7, Visual C++ 2013 schrieb:

    nl: 57408000
    endl: 56628000
    nl/endl: 1.01377

    std::endl ist auf diesem System anscheinend minimal schneller.

    Vista, GCC 4.8.1
    nl/endl: ~0.84

    Bei mir ist das ohne endl bisschen schneller und außerdem spart man sich ein bisschen Schreibarbeit.



  • Bei mir ist endl auch langsamer:

    auto nl = measure_n_times(n, [] { std::cout << "Hello, world!\n"; });
    std::cout.flush();
    auto endl = measure_n_times(n, [] { std::cout << "Hello, world!" << std::endl; });
    

    nl/endl: Schwankt zwischen 0.76 und 0.84

    auto endl = measure_n_times(n, [] { std::cout << "Hello, world!" << std::endl; });
    std::cout.flush();
    auto nl = measure_n_times(n, [] { std::cout << "Hello, world!\n"; });
    

    nl/endl: Durchgängig ~0.70

    CPU: i7-3930k
    OS: Win7 x64, unter WOW64
    GCC: 4.7.1 (glaub ich)

    Btw., für C++11 "monotonic_clock" statt "steady_clock".



  • Mit GCC 4.6 auf einem Linux bekomme ich bei Ausgabe ins Terminal auch 84%.
    Wenn die Standardausgabe stattdessen nach /dev/null geht, ist das ungefähr zehn mal so schnell für endl und das Verhältnis sinkt auf 31%.

    Für GCC 4.6 muss man monotonic_clock nehmen, weil das da noch nicht umbenannt war.

    EDIT: Es ist also in der Praxis nicht egal was man verwendet. Für mich persönlich ist der Unterschied auf Linux so gravierend, dass ich nie endl benutzen würde, wo auch \n ginge.



  • Also bei mir kommt es stark auf die Anzahl Zeichen an. Je mehr Zeichen, desto ausgeglichener ist es bei mir. Wenn ich mal bei 10.000 Zeichen bin, macht es praktisch kein Unterschied mehr.

    #include <chrono>
    #include <fstream>
    #include <iostream>
    #include <string>
    using namespace std;
    
    class stopwatch
    {
    	private:
    		chrono::time_point<chrono::high_resolution_clock> last_;
    	public:
    		stopwatch()
    		{
    			start();
    		}
    		void start()
    		{
    			last_ = chrono::high_resolution_clock::now();
    		}
    		chrono::milliseconds::rep elapsed() const
    		{
    			auto t = chrono::high_resolution_clock::now();
    			return chrono::duration_cast<chrono::milliseconds>(t - last_).count();
    		}
    };
    
    template <class Action>
    double measure_n_times(long long n, Action const &action)
    {
    	stopwatch sw;
    	sw.start();
    	for (long long i = 0; i < n; ++i)
    	{
    		action();
    	}
    	return sw.elapsed();
    }
    
    string random_string( size_t n )
    {
    	const char* const chars = "abcdefghijklmnopqrstuvwxyz"
    			                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    							  "0123456789";
    	const size_t length = strlen( chars );
    
    	string str;
    	str.resize( n );
    	for( size_t i=0; i!=n; ++i )
    	{
    		str[i] = chars[ rand() % length ];
    	}
    	return str;
    }
    
    int main()
    {
    	srand( time(NULL) );
    	const auto min_length = 100;
    	const auto max_length = 1000;
    	const auto runs       = 100000;
    
    	for(auto anz_chars=min_length; anz_chars!=max_length; anz_chars+=min_length)
    	{
    		ofstream file( "test.txt" );
    		auto time = measure_n_times(runs, [&] { file << random_string(anz_chars) << endl; });
    		cout << time << " ms bei " << anz_chars << " Zeichen mit endl." << '\n';
    	}
    
    	cout << '\n' << '\n';
    
    	for(auto anz_chars=min_length; anz_chars!=max_length; anz_chars+=min_length)
    	{
    		ofstream file( "test.txt" );
    		auto time = measure_n_times(runs, [&] { file << random_string(anz_chars) << '\n'; });
    		cout << time << " ms bei " << anz_chars << " Zeichen mit \\n." << '\n';
    	}
    
    	cin.get();
    }
    


  • Kellerautomat schrieb:

    endl ist immer Boese, weil cin zu cout getied ist. Flushen ist daher nie noetig

    die meisten meiner Programme besitzen kein einziges cin.


  • Mod

    otze schrieb:

    Kellerautomat schrieb:

    endl ist immer Boese, weil cin zu cout getied ist. Flushen ist daher nie noetig

    die meisten meiner Programme besitzen kein einziges cin.

    👍

    Bin gerade am Flughafen, daher unter Windows, GCC 4.8 ergibt bei mir 74%.



  • tie und line-buffering ist abschaltbar, std::endl ist es nicht. Also pro '\n'!



  • otze schrieb:

    ist das heute noch wahr? Wenn ja, dann könnten wir in diesem FOrum ein und für alle mal beshcließen, dass cout<<x<<endl; nicht böse ist.

    lol


Log in to reply