Buffered and unbuffered stream
Asked Answered
V

5

6

In case of buffered stream it said in a book that it wait until the buffer is full to write back to the monitor. For example:

cout << "hi"; 
  1. What do they mean by "the buffer is full".

    cerr << "hi";
    
  2. It is said in my book that everything sent to cerr is written to the standard error device immediately, what does it mean?

    char *ch;
    cin>> ch; // I typed "hello world";
    
  3. In this example ch will be assigned to "hello" and "world" will be ignored does it mean that it still in the buffer and it will affect the results of future statements?

Vereen answered 9/5, 2012 at 14:53 Comment(5)
Standard note: by default cout is synchronized with stdio, and by default stdio is line-buffered.Skysail
Actually, in the second example you'll write into memory at an unknown location and cause undefined behavior...**hopefully** resulting in a crash.Unfolded
It is Undefined Behavior in cin >> ch since ch is a pointer to a character string and not initialized yet so it is a wild pointer. Second: using the extraction operator >> with std::cin will stop reading when the first white-space is reached and the remaining of input is still in the buffer.Venal
@YakovGalka just out of curiosity can we change this line-buffered behavior to something else?Fike
@SaurabhRana: Yes. Calling setvbuf on stdout/stderr can control their buffering (no/line/full). Based off my reading of the standards, as long as cout/cerr are synchronized with stdio (the default), this should effectively set their buffering too.Skysail
F
15

Your book doesn't seem very helpful.

1) The output streams send their bytes to a std::streambuf, which may contain a buffer; the std::filebuf (derived from streambuf) used by and std::ofstream will generally be buffered. That means that when you output a character, it isn't necessarily output immediately; it will be written to a buffer, and output to the OS only when the buffer is full, or you explicitly request it in some way, generally by calling flush() on the stream (directly, or indirectly, by using std::endl). This can vary, however; output to std::cout is synchronized with stdout, and most implementations will more or less follow the rules of stdout for std::cout, changing the buffering strategy if the output is going to an interactive device.

At any rate, if you're unsure, and you want to be sure that the output really does leave your program, just add a call to flush.

2) Your book is wrong here.

One of the buffering strategies is unitbuf; this is a flag in the std::ostream which you can set or reset (std::ios_base::set() and std::ios_base::unset()std::ios_base is a base class of std::ostream, so you can call these functions on an std::ostream object). When unitbuf is set, std::ostream adds a call to flush() to the end of every output function, so when you write:

std::cerr << "hello, world";

the stream will be flushed after all of the characters in the string are output, provided unitbuf is set. On start-up, unitbuf is set for std::cerr; by default, it is not set on any other file. But you are free to set or unset it as you wish. I would recommend against unsetting it on std::cerr, but if std::cout is outputting to an interactive device, it makes a lot of sense to set it there.

Note that all that is in question here is the buffer in the streambuf. Typically, the OS also buffers. All flushing the buffer does is transfer the characters to the OS; this fact means that you cannot use ofstream directly when transactional integrity is required.

3) When you input to a string or a character buffer using >>, the std::istream first skips leading white space, and then inputs up to but not including the next white space. In the formal terms of the standard, it "extracts" the characters from the stream, so that they will not be seen again (unless you seek, if the stream supports it). The next input will pickup where ever the previous left off. Whether the following characters are in a buffer, or still on disk, is really irrelevant.

Note that the buffering of input is somewhat complex, in that it occurs at several different levels, and at the OS level, it takes different forms depending on the device. Typically, the OS will buffer a file by sectors, often reading several sectors in advance. The OS will always return as many characters as were demanded, unless it encounters end of file. Most OSs will buffer a keyboard by line: not returning from a read request until a complete line has been entered, and never returning characters beyond the end of the current line in a read request.

In the same manner as std::ostream uses a streambuf for output, std::istream uses one to get each individual character. In the case of std::cin, it will normally be a filebuf; when the istream requests a character, the filebuf will return one from its buffer if it has one; if it doesn't, it will attempt to refill the buffer, requesting e.g. 512 (or whatever its buffer size is) characters from the OS. Which will respond according to its buffering policy for the device, as described above.

At any rate, if std::cin is connected to the keyboard, and you've typed "hello world", all of the characters you've typed will be read by the stream eventually. (But if you're using >>, there'll be a lot of whitespace that you won't see.)

Finbar answered 9/5, 2012 at 15:25 Comment(0)
B
2

streams in C++ are buffer to increase efficiency, that is file and console IO is very slow in comparison to memory operations.

To combat this C++ streams have a buffer (a bank of memory) that contains everything to write to the file or output, when it is full then it flushed to the file. The inverse is true for input, it fetches more when it the buffer is depleted.

This is very import for streams because the following

std::cout << 1 << "hello" << ' ' << "world\n";

Would be 4 writes to a file which is inefficient.

However in the case of std::cout, cin, and cerr then these type actually have buffering turned off by default to ensure that it can be used in conjunction with std::printf and std::puts etc...

To re-enable it (which I recommend doing):

std::ios_base::sync_with_stdio(false);

But don't use C style console output whilst it is set false or bad things may happen.

Blankly answered 9/5, 2012 at 15:1 Comment(4)
so in your example "1hello world\n" will be first passed to the buffer and then to the screen. but if I did cerr<< 1 << "hello" << ' ' << "world\n"; then 1 will be passed to the screen and then "hello" and so on... is this correct? thanksVereen
@Vereen well "1hello world\n" will be written to the buffer, it will be written to the screen when either input is want from the user, cin extraction operations sync the two streams causing cout to flush. When you manually flush the stream using std::cout << std::flush or std::cout << std::endl; Or when the stream is destroyed & closed (IE end of program for std::cout or end of scope for files).Blankly
@Vereen Cerr and Cout differ by the file descriptor they are sent too, cout goes to stdout and cerr goes to stderror. when you set them not to sync with stdio then they both buffer, when it is true then they both are unbuffered. That said clog is the same as cerr but I believe is always buffered.Blankly
@Vereen I have just tried this with GCC and it seems that in GCC that std::cerr is always unbuffered even with std::ios_base::sync_with_stdio(false).Blankly
K
2

You can check out the differences yourself with a small app.

#include <iostream>
int main() {
    std::cout<<"Start";
    //std::cout<<"Start"<<std::endl; // line buffered, endl will flush.
    double j = 2;
    for(int i = 0; i < 100000000; i++){
        j = i / (i+1);
    }
    std::cout<<j;
    return 0;
}

Try out the difference of the two "Start" statments, then change to cerr. The difference you notice is due to buffering.

The for-statement takes about 2 seconds on my rig, you might need to tweak the i < condition on yours.

Krumm answered 9/5, 2012 at 15:13 Comment(4)
sorry but I get the same result. Start and then after 1second I get 0Vereen
If you get that result from the way it is written here, you have an unbuffered std::cout.Krumm
Or a very slow console. Like, on windows :)Misvalue
+1 for the example. Better than an illustration, as they say :)Sublittoral
W
1

1) what do they mean by "the buffer is full".

With buffered output there's a region of memory, called a buffer, where the stuff you write out is stored before it is actually written to the output. When you say cout << "hi" the string is probably only copied into that buffer and not written out. cout usually waits until that memory has been filled up before it actually starts writing things out.

The reason for this is because usually the process of starting to actually write data is slow, and so if you do that for every character you get terrible performance. A buffer is used so that the program only has to do that infrequently and you get much better performance.

2) it said in my book that everything sent to cerr is written to the standard error device immediatly, does this mean it send 'h' and then 'i'...?

It just means that no buffer is used. cerr might still send 'h' and 'i' at the same time since it already has both of them.

3)in this example ch will be assigned to "hello" and "world" will be ignored does it mean that it still in the buffer and it will affect the results of future statements ?

This doesn't really have anything to do with buffering. the operator >> for char* is defined to read until it sees whitespace, so it stops at the space between "hello" and "world". But yes, the next time you read you will get "world".

(although if that code isn't just a paraphrase of the actuall code then it has undefined behavior because you're reading the text into an undefined memory location. Instead you should do:

std::string s;
cin >> s;

)

Woald answered 9/5, 2012 at 15:12 Comment(0)
S
0
  1. Each call to write to the terminal is slow, so to avoid doing slow things often the data is stored in memory until either a certain amount of data has been entered or the buffer is flushed manually with fflush or std::endl. The result of this is sometimes that text might not be written to the terminal at the moment you expect it to.

  2. Since the timing of error messages is more critical than normal output, the performance hit is ignored and the data is not buffered. However, since a string is passed in a single piece of data, it is written in one call (inside a loop somewhere).

  3. It world would still be in the buffer, but it's quite easy to prove this yourself by trying it in a 3 line program. However, your example will fail since you are attempting to write into unallocated memory. You should be taking input into a std::string instead.

Sibylsibylla answered 9/5, 2012 at 15:10 Comment(3)
Point 2 isn't really correct. The filebuf of std::cerr still buffers as usual. The difference is that unitbuf is set, which means that the ostream does a flush at the end of each >>. (In fact, in the destructor of the sentry object constructed at the top of the function.)Finbar
@JamesKanze are the only things that make the buffer flushed is fflush ot std::endl; ? what about cout << "hello"; cout << "world";Vereen
@Vereen No. Unless unitbuf is set, in which case, there will be a flush at the end of every '<<'.Finbar

© 2022 - 2024 — McMap. All rights reserved.