Why does using std::endl with ostringstream affect output speed?
Asked Answered
M

3

10

I'm timing the difference between various ways to print text to standard output. I'm testing cout, printf, and ostringstream using both \n and std::endl. I expected std::endl to make a difference with cout (and it did), but I didn't expect it to slow down output with ostringstream. I thought using std::endl would just write a \n to the stream and it would still only get flushed once. What's going on here? Here's all my code:

// cout.cpp
#include <iostream>

using namespace std;

int main() {
  for (int i = 0; i < 10000000; i++) {
    cout << "Hello World!\n";
  }
  return 0;
}

// printf.cpp
#include <stdio.h>

int main() {
  for (int i = 0; i < 10000000; i++) {
    printf("Hello World!\n");
  }
  return 0;
}

// stream.cpp
#include <iostream>
#include <sstream>

using namespace std;

int main () {
  ostringstream ss;
  for (int i = 0; i < 10000000; i++) {
    ss << "stream" << endl;
  }
  cout << ss.str();
}

// streamn.cpp
#include <iostream>
#include <sstream>

using namespace std;

int main () {
  ostringstream ss;
  for (int i = 0; i < 10000000; i++) {
    ss << "stream\n";
  }
  cout << ss.str();
}

And here's my Makefile

SHELL:=/bin/bash

all: cout.cpp printf.cpp
    g++ cout.cpp -o cout.out
    g++ printf.cpp -o printf.out
    g++ stream.cpp -o stream.out
    g++ streamn.cpp -o streamn.out
time:
    time ./cout.out > output.txt
    time ./printf.out > output.txt
    time ./stream.out > output.txt
    time ./streamn.out > output.txt

Here's what I get when I run make followed by make time

time ./cout.out > output.txt

real    0m1.771s
user    0m0.616s
sys 0m0.148s
time ./printf.out > output.txt

real    0m2.411s
user    0m0.392s
sys 0m0.172s
time ./stream.out > output.txt

real    0m2.048s
user    0m0.632s
sys 0m0.220s
time ./streamn.out > output.txt

real    0m1.742s
user    0m0.404s
sys 0m0.200s

These results are consistent.

Muskellunge answered 1/10, 2012 at 14:29 Comment(7)
This will probably answer your question: https://mcmap.net/q/63456/-quot-std-endl-quot-vs-quot-n-quot In short: yes, std::endl flushes the outputDonndonna
I don't know if I read this question wrong, but I assumed he was asking why flushing the stringstream would have a performance issue. What extra work does flushing a stringstream do that couldn't just be left to the call to .str()? For cout, rendering to the terminal will take time, the are there any visible side-effects of flushing a stringstream?Deneb
@Donndonna I've actually read that question, but in part I'm wondering what it means to flush a stringstream. I thought that it would essentially do nothing, but apparently I'm wrong.Muskellunge
@gsingh2011 It's still a function call, even if the function might not do anything. Nevertheless, there will probably be a noticable overhead without any optimization.Donndonna
@Donndonna I optimized with O3 and the relative effiencies of each method are the same.Muskellunge
What compiler and std implementation was tested here?Development
@Development Good question. g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3Muskellunge
H
16

std::endl triggers a flush of the stream, which slows down printing a lot. See http://en.cppreference.com/w/cpp/io/manip/endl

It is often recommended to not use std::endl unless you really want the stream to be flushed. If this is really important to you, depends on your use case.

Regarding why flush has a performance impact even on a ostringstream (where no flushing should happen): It seems that an implementation is required to at least construct the sentry objects. Those need to check good and tie of the ostream. The call to pubsync should be able to be optimized out. This is based on my reading of libcpp and libstdc++.

After some more reading the interesting question seems to be this: Is an implementation of basic_ostringstream::flush really required to construct the sentry object? If not, this seems like a "quality of implementation" issues to me. But I actually think it needs to because even a basic_stringbug can change to have its badbit set.

Hepza answered 1/10, 2012 at 14:31 Comment(2)
I actually knew that, but I didn't know that it affected the performance of stringstream. Flushing cout means outputting whatever is in the buffer to output, what does flushing stringstream mean? What is the output that the text gets flushed to?Muskellunge
@gsingh2011 That's what I would think. But basic_ostringstream has in fact basic_stringbuf attached and is required to construct the sentry mentioned in the documentation of flush.Hepza
A
1

Each output operation on a stream dors multiple steps:

  • It checks if the stream is in good shape.
  • It checks if some other stream needs to flushed.
  • The sequence of characters is produced.
  • It checks if there is enough space for the written characters.
  • The use of endl calls an extra virtual function on the stream buffer.

I would personally expect that the extra virtual function call actually has relativly small impact compared to the other operations. You can verify this guess by also profiling this output:

out << "stream" << '\n';

... or even

out << "stream" << out.widen('\n');

That said, there are a number of improvements which a stream implementation can apply to cut down on checks. Whether this is done will depend on the implementation, of course.

Aynat answered 1/10, 2012 at 17:27 Comment(0)
U
0

Using std::endl is equivalent to writing

stream << "\n";
stream.flush();

Don't use std::endl unless you actually want to trigger flushing, and / or don't care about output performance.

Also, don't worry about different line endings on different platforms, your implementation will translate "\n" to the line ending appropriate for your platform.

Unlovely answered 1/10, 2012 at 14:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.