I want to have a clear example that actually using the _IONBF macro in the setvbuf function, the writing operations (stream) in a file take place directly, without using the buffer. If I did this experiment in my file system (SSD memory) I would not see any delay in writing as it is a very fast device, but if I did this using a floppy, I would have to see, when I run the program, the command prompt of my terminal, release as soon as writing to the floppy disk ends.
Why does it seem to me that writing still occurs through a buffer?
note: in the program string I made a copy / paste of a very long text (1.44 Mbyte of characters); so if you want to try the experiment (in addition to a floppy drive), you have to fill that string enough to make writing on the device slow.
#include <stdio.h>
#include <string.h>
int main() {
FILE *fp;
char string[] = "here goes a very long text .... enough to fill a floppy disk!";
fp = freopen("/Volumes/FLOPPY/file.txt", "w", stdout);
setvbuf(fp, NULL, _IONBF, 0);
printf("%s", string);
printf("\nEND OF FILE");
fclose(fp);
return(0);
}
The
printf()function is likely to map your request to a singlewrite()call, not using the buffering, but not slowing down either. If you want to see the effect of not buffering, use single-character I/O:With unbuffered I/O, that will show the difference because each call to
putc()requires awrite()system call because there's only a single byte available to work on.Note that the code below doesn't work with a string — it works with a byte array (no null terminator). The difference is not important for performance; it does require a modicum of care in handling the data.
The code also uses a regular
fopen()rather than usingfreopen()onstdout. Again, that won't (is extremely unlikely to) make a material difference to performance.slow11.cslow61.cI also created a program
slow61.cfromslow11.cby removing thesetvbuf()line.fast89.cSample run times
The
timecmdprogram is a home-brew; the-uoption reports times in microseconds (-nfor nanoseconds;-mfor milliseconds, nothing for whole seconds). It's primarily for long-running programs where it helps to know when the command started so you can guess when it will finish (which is why it reports the start and finish information). You can probably use (POSIX standard)timeinstead; the output format will be different.It doesn't take much skill to see that the
slow11code takes about 300 times as long as thefast89code, nor that the non-buffering is the key factor sinceslow61is only about 6-7 times as slow asfast89.Testing on a MacBook Pro running macOS Mojave 10.14.6 with SSD and using GCC 9.2.0.