Search code examples
cperformanceiofwrite

fwrite() - effect of size and count on performance


There seems to be a lot of confusion regarding the purpose of the two arguments 'size' and 'count' in fwrite(). I am trying to figure out which will be faster -

fwrite(source, 1, 50000, destination);

or

fwrite(source, 50000, 1, destination);

This is an important decision in my code as this command will be executed millions of times.

Now, I could just jump to testing and use the one which gives better results, but the problem is that the code is intended for MANY platforms.

So,

  • How can I get a definitive answer to which is better across platforms?

  • Will implementation logic of fwrite() vary from platform to platform?

I realize there are similar questions (What is the rationale for fread/fwrite taking size and count as arguments?, Performance of fwrite and write size) but do understand that this is a different question regarding the same issue. The answers in similar questions do not suffice in this case.


Solution

  • The performance should not depend on either way, because anyone implementing fwrite would multiply size and count to determine how much I/O to do.

    This is exemplified by FreeBSD's libc implementation of fwrite.c, which in its entirety reads (include directives elided):

    /*
     * Write `count' objects (each size `size') from memory to the given file.
     * Return the number of whole objects written.
     */
    size_t
    fwrite(buf, size, count, fp)
        const void * __restrict buf;
        size_t size, count;
        FILE * __restrict fp;
    {
        size_t n;
        struct __suio uio;
        struct __siov iov;
    
        /*
         * ANSI and SUSv2 require a return value of 0 if size or count are 0.
         */
        if ((count == 0) || (size == 0))
            return (0);
    
        /*
         * Check for integer overflow.  As an optimization, first check that
         * at least one of {count, size} is at least 2^16, since if both
         * values are less than that, their product can't possible overflow
         * (size_t is always at least 32 bits on FreeBSD).
         */
        if (((count | size) > 0xFFFF) &&
            (count > SIZE_MAX / size)) {
            errno = EINVAL;
            fp->_flags |= __SERR;
            return (0);
        }
    
        n = count * size;
    
        iov.iov_base = (void *)buf;
        uio.uio_resid = iov.iov_len = n;
        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;
    
        FLOCKFILE(fp);
        ORIENT(fp, -1);
        /*
         * The usual case is success (__sfvwrite returns 0);
         * skip the divide if this happens, since divides are
         * generally slow and since this occurs whenever size==0.
         */
        if (__sfvwrite(fp, &uio) != 0)
            count = (n - uio.uio_resid) / size;
        FUNLOCKFILE(fp);
        return (count);
    }