C++ buffered stream IO
Asked Answered
S

3

6

I understand that by default all stream IO supported by C++ is buffered.

This means that data to be output is put into a buffer till it is full and then sent to the output device, similarly for input, the data is read once the buffer is empty...all this is done so that number of expensive system calls could be minimized.

But how to verify this behavior in action. I mean consider the following code

int main()
{
    cout << "Hello world\n";
    return 0
}

Where does buffering come into picture here ? I know there is buffering happening, but how to explain it? The output is seen instantly on the screen, so what could be a code example to actually see buffered I/O in action ?

Subchloride answered 9/7, 2012 at 9:40 Comment(5)
In the case of cout, the buffer is 1 byte. Real buffering comes in to play when you read/write files .Scintillator
@Scintillator How do you make that out? And no, it’s not necessarily true.Almita
@KonradRudolph Well I don't remember the source of that saying but it's some what happens in most caseScintillator
@Scintillator There is no case in the standard streams where a buffer of 1 byte is used. std::cout is buffered normally. (This is different from stdout in C, which is line buffered if it is connected to an interactive device, and fully buffered otherwise. C++ doesn't have the concept of line buffering; using std::endl effectively simulates it.)Jaret
@JamesKanze the default case is in fact C++ streams not being buffered, because instead C++ streams write straight to C stdio streams, which is how they can be synchronized. It is only buffered if you desynchronize them explicitly.Cheju
A
5

Try the following program. sleep(1) is used to introduce delay(1 second), I'm using linux, so sleep works for me. If you can't make it work, try other ways to delay this program(for example, simple for loop). You may also try increasing buffer size(uncomment commented lines of code) if you don't see any buffering effect.

On my OS(Linux 3.2.0) and compiler(g++ 4.6.3), this program prints "Portion1Portion2" then "Portion3Portion4" and then "Portion5". std::endl guaranteed to flush buffer, but as you can see, newline character also works this way for me.

#include <iostream>
#include <unistd.h>

using namespace std;

int main () {
    // Try uncommenting following lines to increase buffer size
    // char mybuf[1024];
    // cout.rdbuf()->pubsetbuf(mybuf, 1024);

    cout << "Portion1";
    sleep(1);
    cout << "Portion2\n";
    sleep(1);
    cout << "Portion3";
    sleep(1);
    cout << "Portion4" << endl;
    sleep(1);
    cout << "Portion5" << endl;
    sleep(1);
    cout << "Done!" << endl;

    return 0;
}
Avril answered 9/7, 2012 at 10:3 Comment(1)
Even when lines are uncommented?Avril
J
9

First, not all iostream is buffered; buffering is handled by the attached streambuf. In the case of filebuf (used by ifstream and ofstream), input will read as much as possible, up to the size of the buffer, and output will flush the buffer on overflow, when an explicit flush or close occurs, or when the object is destructed (which implicitly calls close).

The case of cout is a bit special, since it is never destructed nor closes. There is a guarantee from the system that flush will be called on it at least once after exit is called (which is what happens when you return from main). This means that any output before returning from main will be flushed; if you're using cout in destructors of static objects, you still need an explicit flush to be sure.

It's also possible to tie an output stream to an input stream; cout is tied to cin by default. In this case, any attempt to input from the tied stream will flush the output.

The usual convention is to just use std::endl instead of simply outputting '\n'; std::endl outputs a '\n' and then flushes the stream. For streams where it is very important for all output to appear promptly, there is a unitbuf flag which can be set, which means that the stream will be flushed at the end of each << operator. (std::cerr has this set by default.)

Finally, if you want to see the effect of buffering, put something like sleep(10) after your output. If it output appears immediately, it has been flushed; if it doesn't it has been buffered, and the flush occured implicitly after the sleep.

Jaret answered 9/7, 2012 at 10:12 Comment(6)
Thanks for replying. But it would be great if you could provide me with a code example which consistently works.Subchloride
@Subchloride Which consistently works to do what? Using std::endl instead of '\n' will cause a flush at the end of each line. If you need more than that, std::cout << std::flush will flush the output unconditionally.Jaret
I need an example which depicts the role of buffer. Something like cout << "Hello world"; //Here output is not seen on console cout << endl; // Now Hello world is seen on console.Subchloride
@Subchloride So put a delay between those two statements, and you should see that none of the output occurs until after the delay.Jaret
Tried that with no luck on VS2008 and VS2010. The statements are immediately output on the screen. So no evidence of buffering is observed.Subchloride
@Arunm So maybe VC2010 uses a very small buffer. Or changes its buffering policy if the output device is interactive. (Or, what I think, that the compiler ends up doing unit buffering because of the way it implements synchronization with stdio.)Jaret
A
5

Try the following program. sleep(1) is used to introduce delay(1 second), I'm using linux, so sleep works for me. If you can't make it work, try other ways to delay this program(for example, simple for loop). You may also try increasing buffer size(uncomment commented lines of code) if you don't see any buffering effect.

On my OS(Linux 3.2.0) and compiler(g++ 4.6.3), this program prints "Portion1Portion2" then "Portion3Portion4" and then "Portion5". std::endl guaranteed to flush buffer, but as you can see, newline character also works this way for me.

#include <iostream>
#include <unistd.h>

using namespace std;

int main () {
    // Try uncommenting following lines to increase buffer size
    // char mybuf[1024];
    // cout.rdbuf()->pubsetbuf(mybuf, 1024);

    cout << "Portion1";
    sleep(1);
    cout << "Portion2\n";
    sleep(1);
    cout << "Portion3";
    sleep(1);
    cout << "Portion4" << endl;
    sleep(1);
    cout << "Portion5" << endl;
    sleep(1);
    cout << "Done!" << endl;

    return 0;
}
Avril answered 9/7, 2012 at 10:3 Comment(1)
Even when lines are uncommented?Avril
B
4

Try the following code:

int main()
{
    for( int i =0 ; i < 10; i ++ )
    {
        cout << i << " ";
        cerr << i << " ";
    }
}

The buffered output is usually flushed with the destruction of the stream object, so the code above will print (not always, ofc, but it does for me with gcc 4.6.3)

0 1 2 3..9
0 1 2 3..9

instead of

0 0 1 1 2 2 3 3 .... 9 9 

my output

Because the unbuffered cerr is printed right away (first sequence) , and buffered cout is printed in the end of main().

Bettiebettina answered 9/7, 2012 at 9:44 Comment(7)
@Arun, That's why I said "not always". Buffering is implementation-specific, so you will see different results with different compilers and settings. Sometimes the outputs can even get mixed in a weird order.Bettiebettina
std::cout is never destructed, but buffers can be flushed for other reasons. In this case, both std::cin and std::cerr are tied to std::cout, which means that any IO on one of them will flust std::cout. And std::cerr is unit buffered, which means that every << operator flushes it at the end of the operation.Jaret
@Bettiebettina is there no consistent example which will work always? I tried all the examples in the question, none are working...does this mean for VS2010, it flushes the buffers always?Subchloride
@Arun, sorry, I'm unaware of a consistent example. I've posted an image of my output, check the answer please.Bettiebettina
@Bettiebettina It may (probably) depends on the version of the library. C++11 requires std::cout to be tied to std::cerr (in addition to std::cin); this means that any output to std::cerr will flush std::cout, giving "0 0 1 1 ...". C++03 didn't allow it to be tied, so will typically give "0 1 2... 0 1 2...".Jaret
@JamesKanze, Yes, that sounds plausible. Maybe that also depends on optimization settings.Bettiebettina
@Bettiebettina Optimization has nothing to do with the semantics implemented in the library. The standard very definitely requires different behavior (with regards to the tie) here. A C++03 implementation could flush, because it used a buffer of 3 bytes, say, but that would be independent of any output on std::cerr. A C++11 implementation must flush, regardless of buffer size, as soon as anything is output to std::cerr.Jaret

© 2022 - 2024 — McMap. All rights reserved.