Hello again! In this video, we are going to look at streams and buffering.
We have already mentioned that C++ streams use buffering.
The purpose of this is to reduce the number of calls to the operating system.
When our program calls the operating system, it has to stop and wait. And then for input and output,
the operating system has to stop and wait for the disk to do whatever it has to do.
So, for example, during a write operation, the stream will store the data in a memory buffer.
The size of this buffer is equal to the maximum amount of data that the operating system will accept
in a single call.
When the buffer is full, that means we have the optimal amount of data. And then the stream will remove
the data from the buffer and send it off to the operating system.
And that process is known as "flushing"
the buffer. "Flushing" basically means getting rid of something, so it is now somebody else's problem.
When does this take place?
It depends on the type of the stream. For ostream, like cout,
it depends on the operating system, and also on how the user has set their terminal up.
The usual arrangement is to have what is called "line oriented" output.
So every time the terminal sees a new line, it will print all the data it has. If you only give it
part of a line, it will wait until it gets the new line character.
Also, C++ requires that if the program is going to read from cin, it must first flush cout. So that
makes sure that the user can see all the output that the program has generated, before they are required
to respond to it.
For output file streams, the buffer is only flushed when it is full. And for input streams, there is,
surprisingly, no way to actually flush them directly.
If you want to remove data from a stream, you have to read it.
And that is normally good enough for most programs. We do not really care too much when the output appears
on the screen or gets written to the disk, so long as it is there when we need it.
But there are some times when we do need to have control over it. The library provides std flush
for that.
This is not a function call.
It is actually something called a manipulator. A stream manipulator.
So if we have some stream operation, for example, we are sending the variable i to cout, and then we put
flush after
that. Then that will cause the cout buffer to be flushed.
And that will cause the value of i to appear on the screen immediately.
So this means that the output on the screen, or the data in the file, always matches exactly what the
program thinks it has sent off.
On the other hand, this does have a big impact on performance, because we are doing a lot more hardware
operations.
So you should only use flush if the data really needs to be up to date.
One example of that is if you have a program that keeps crashing, and you decide to create a log file.
So you can find out what the program was doing when it crashed.
So every time the program does something, it will write an entry in this file saying what it is doing,
what the value of the data is, and so on.
And then, when the program crashes, you can look at this file and try to work out what the problem is.
If you let the stream decide when the buffer is flushed, it is possible that the program could crash
when there is still a lot of data in the buffet.
So the actual data in the file could be quite a bit out of date.
It could be several operations before the one that causes the problem.
So what you want is to make sure that the data in the file exactly matches what the program is doing.
And for that, you would need to call flush.
So every time the program writes to the file, the file will contain the data about what it is doing.
It does not get delayed or held back.
So let's look at a simple example of this.
We have a loop. And on each loop iteration, we are going to display the value of the loop counter and
also write it to a file.
So we've done the usual thing.
We create an ofstream object and check that is valid.
And then partway through the loop, we kill the program.
So what will happen?
cout is presumably line buffered. So this should display the correct value. Every time we send this
value to cout, it will appear on the display. The output file stream is not line buffered.
So this is going to be the last value from when the buffer was flushed. And that may or may not contain
the right value for when the program stopped.
So let's try this out.
And this takes a long time to write.
We need to make sure that this number is greater than the maximum possible buffer size.
I think it is normally 4000 bytes on Unix, but I am not sure how big it is on Windows. But this is big
enough anyway.
So there we are, the program is now terminated.
And on the display, we see 66,666.
So that is the correct value.
So the display does get updated every time we send
i followed by a new line.
As for the log file - well, we do not get the correct value. It got as far as 66021,
and then that must be where the buffer was flushed. And then the rest of that number and all the following
numbers were presumably in a buffer, and have now been lost forever.
If I now put in a flush afterwards...
So the terminal is going to display the value of i, followed by a new line every time we send it,
but it was doing that anyway.
And this means that the file stream's buffer is going to be flushed every time we send i and a new line
to it.
So let's see what difference that makes...
And let's jump ahead a bit!
So there we are.
It is...
It actually takes longer to run because of all these file operations.
So there it is, we get 66,666 again. So that has made no difference to the
terminal.
And the log file now goes all the way down to 66,666.
So this log file is being updated on every iteration through the loop.
There is another manipulator you might have seen before, called endl.
And this is equivalent to doing new line, followed by flush.
So this should give the same results.
And there we are. The same results for the terminal.
And the same results in the log file.
Okay, so that's it for this video.
I'll see you next time.
But meanwhile, keep coding!