fwrite() - size和count对性能的影响

25

在fwrite()函数中,关于参数'size'和'count'的用途似乎存在很多混淆。我正在努力弄清楚哪个更快 -

fwrite(source, 1, 50000, destination);
或者
fwrite(source, 50000, 1, destination);

在我的代码中,这是一个重要的决定,因为该命令将被执行数百万次。

现在,我可以直接进行测试并使用提供更好结果的命令,但问题是该代码旨在用于许多平台。

那么,

  • 如何获得跨平台的明确答案?

  • fwrite() 的实现逻辑会因平台而异吗?

我意识到有类似的问题(fread/fwrite 为什么需要 size 和 count 作为参数?fwrite 和写入大小的性能),但请理解这是关于同一问题的不同问题。在类似问题的答案在这种情况下不足够。


1
我刚在OS-X上进行了一些测试,写入了十个100MB的文件,无论参数的顺序如何,或者使用write(2)而不是fwrite,都没有任何区别。至于其他平台,我无法确定。 - William Morris
3个回答

15

如果您考虑返回值,即成功写入/读取流的对象计数,则两个参数的目的变得更清晰:

fwrite(src, 1, 50000, dst); // will return 50000
fwrite(src, 50000, 1, dst); // will return 1

速度可能会因实现而异,但我不期望有任何显著的差异。


3
同意。特别是,如果第一个选项只获取49999个字节并返回0,则会失败;如果第二个选项读取的字节数为49999,则会返回49999。因此,如果您的数据严格为50,000字节,则使用哪种选项都无关紧要。如果您的数据不是严格的50,000字节,而可能更短,则需要使用更灵活的选项。 - Jonathan Leffler
我错误地使用了 fread 而不是 fwrite,但无论如何都适用相同的原则。 - Aleš Kotnik

15

性能不应该依赖于任何一种方式,因为任何实现fwrite的人都会将size和count相乘以确定要执行多少I/O操作。

这可以通过FreeBSD的libc实现的fwrite.c来说明,它完全读取(包括忽略指令):

/*
 * 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);
}

1
我在多个平台上运行了测试,并查看了其他一些平台的fwrite源代码,但它们之间没有任何性能差异。谢谢! - Shailesh Tainwala

2
我想指向我的问题,这个问题揭示了调用fwrite一次和调用多次的性能差异。我的问题是Microsoft对fwrite的实现有一个缺陷,无法在一次调用中写入大于4GB的文件(在fwrite处挂起)。因此,我不得不通过循环调用fwrite来分块写入文件,直到数据完全写入。我发现后一种方法总是比单一的fwrite调用更快返回。我使用Windows 7 x64,有32 GB的RAM,这使得写缓存非常积极。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接