3

The declaration of fread is as following:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

Code:

char a[1000];
//1
fread(a, 1, 1000, stdin); 
//2
fread(a, 1000, 1, stdin);

Will the first really physically read 1 byte at a time and read 1000 times from the disc? Can that affect the performance and make the disc more likely to be damaged?

1 Answer 1

5

Abstract:

Disk I/O is complex, fread() provides a simpler interface to that complexity. This answer will take a look at what fread() does, and a further look at the things going on even deeper.


fread():

Declaration: size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

Description:

The function fread() reads nmemb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.

fread() is a wrapper for the read() syscall -- wrapper functions are just regular functions commonly used to provide a better interface to another function, sometimes with extra computation involved, but usually not; fread() is a wrapper in the way that it takes two arguments that eventually is combined into a desired amount, and also some more things that will be explained later.

In short, it's not the fread() function that actually does the reading. The fread() function is merely an interface to read().

char a[1000];

fread(bufr, 1, 1000, filehandle); 
fread(bufr, 1000, 1, filehandle);

The nmemb and size parameters purpose is to provide an intuitive interface for the user, i.e., if size is set to 1 and the nmemb parameter is set to 100 it will return 100 on success, whereas if the size parameter set to 100 and nmemb is then set to 1 the function will still only return 1, even though it reads the same amount of bytes. So somewhere in the implementation of fread() is a return statement that (should) return a value equal to nmemb bytes.

The function read() will be called inside a while loop, that has the condition of count != 0, count is the number of bytes left to read. Here is the call read(fileno(stream), data, nbytes);, the first argument would be filehandle in our earlier call to fread(), the second is a pointer to the area in memory to be read, the third is the total amount to read.

A full implementation of the stdio library can be found here (use CTRL+F to find fread). Note: There are many implementations and versions of the stdio library, as such not all of them will use read(), but all POSIX compliant operating systems will use read().

read():

Declaration: ssize_t read(int fd, void *buf, size_t count); Description:

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

On files that support seeking, the read operation commences at the current file offset, and the file offset is incremented by the number of bytes read. If the current file offset is at or past the end of file, no bytes are read, and read() returns zero.

read() is a syscall, i.e., it makes a direct system call to the Linux kernel, instead of going through a wrapper function such as fread(). Anyways, because read() is a syscall the “function” itself does not contain more than a couple lines, which are very cryptic at first glance (and second or even third, really).

A read() implementation can be found here

fread() and read() a real world comparison:

Alright, so we now we know that fread() is is a wrapper function for the syscall read(). The question now is, how do they compare?

I wrote a small program for testing how much CPU time is spent reading from a file, both in chunks of 1 * X bytes and X * 1 bytes, for both read() and fread(). The average results are below but all values can be found here

// Average (or mean value). Time in seconds.


// fread()
0.0144 // 100MB * 1
0.0056 // 1 * 100,000,000


read()
51.31  // 1 * 100,000,000
0.0136 // 100MB * 1

Surprisingly, or maybe not actually, read() is slower than fread() when reading one chunk of 100MB, and when reading 100 million chunks of 1 byte it is as expected a lot slower. Furthermore fread() is about three times faster if you ask it to read a lot of smaller chunks, albeit the real-world difference is negligible; if however nmemb and size are both equal or close to equal, then getrusage() claimed that as little as 0.000000 seconds was spent (plus minus double imprecision, and whatever imprecision getrusage() might bring). Ultimately it looks as if fread() gets its speed from optimized calls to read().

readahead():

Declaration: ssize_t readahead(int fd, off64_t offset, size_t count);

Description

readahead() initiates readahead on a file so that subsequent reads from that file will be satisfied from the cache, and not block on disk I/O (assuming the readahead was initiated early enough and that other activity on the system did not in the meantime flush pages from the cache).

The document I will be referencing here is from 2007 and represents Linux 2.6, the current stable version as of writing is 4.8.6, although readahead() doesn’t seem to have had a substantial update since 2.6.

readahead() works by inspecting all read requests, and attempts to determine whether or not a sequential read is being performed, if it is, readahead() will perform a readahead and store the result in the disk cache. Originally readahead() always read 128KB ahead, 2.6 and later version of readahead() was “adaptive”, that is, it will read up to one MB if a very large file is being read, such as a video. What really goes on behind the scenes, however, is still beyond me, although if still looking for more, https://lwn.net/Articles/155510/, is a good writedown of what went down in the old readahead in comparison to the new from a not too technical standpoint.

I don’t see exactly how readahead() effects read() and fread(), so I will see to perform more experiments when I find the time.

Finishing words

In the end, all this doesn’t directly answer your question; but it should say something about the relationship between read(), fread(), and readahead, it should tell you what they do, and to an extent why. You should at least be able to come up with your own hypothesis to what is going on, which I am sorry to say you have to, because I still haven’t figured all of this out yet, I will come back and further edit this answer, until I consider it complete. At least I learned a lot while researching and writing for this answer, and I hope you did as well.

Although I have read through this multiple times already, I likely missed something, as such I would appreciate if you tell me about anything wrong, whether that is by commenting or editing the answer. Any other criticism is also welcomed.


Sources and further reading:

All sources below should have been referenced once, in no particular order.

3
  • Thank you, Kleiven!
    – Kevin King
    Commented Oct 18, 2016 at 12:40
  • You're very welcome! Oh and for the record, I made a substantial edit to my answer, and will continue to update it further in the future as I find more information, in case you should ever wonder what's really happening. Commented Oct 18, 2016 at 14:05
  • Wow! Thank you, Kleiven! You are amazing!
    – Kevin King
    Commented Oct 18, 2016 at 15:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .