fwrite()性能远低于磁盘容量

时间:2014-02-22 00:13:57

标签: c windows visual-studio-2012 file-io

我有一个动态分配的struct数组,包含1700万个元素。要将其保存到磁盘,我写

fwrite(StructList, sizeof(Struct), NumStructs, FilePointer)

在稍后的步骤中,我使用等效的fread语句阅读它,即使用sizeof(Struct)NumStructs计数。我希望得到的文件大约是3.5 GB(这都是x64)。

是否可以将sizeof(Struct) * NumStructs作为大小而1作为计数来加快速度?我想知道为什么写操作可能会在具有32 GB RAM(大量写缓存)的快速计算机上花费分钟。我已经运行了自制的基准测试,缓存足够激进,前800 MB到1 GB的典型值为400 MB /秒。 PerfMon表明它在fwrite期间消耗了100%的一个核心。

我看到问题here所以我要问的是,fwrite中是否有一些循环可以通过告诉它写出1个大小为n * s的元素而被“欺骗”以加快速度到n个大小为s的元素。

修改

我在发布模式下运行了两次,两次都放弃了等待。然后我在调试模式下运行它,知道通常fwrite操作会占用更长的时间。要写入的数据的确切大小为4,368,892,928字节。在所有这三种情况下,PerfMon显示两次相隔约30秒的磁盘写入活动,之后CPU进入一个核心的100%。该文件位于该点73,924,608字节。我在fwrite的任何一侧都有断点,所以我知道它就在哪里。肯定看来有些东西被卡住了,但我会让它在一夜之间运行并看到。

修改

离开这一夜,它肯定挂在fwrite,文件永远不会超过70 MB。

2 个答案:

答案 0 :(得分:2)

这绝对是fwrite的问题(我尝试了VS2012和2010)。

从标准C ++项目开始,我只更改了设置,以便在静态链接中使用多字节字符集,x64目标和标准库的多线程调试版本。

以下代码成功(没有错误检查以获得简洁性):

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp;
    long long n;
    unsigned char *data;

    n = 4LL * 1024 * 1024 * 1024 - 1;

    data = (unsigned char *)malloc(n * sizeof(unsigned char));

    fp = fopen("T:\\test.bin", "wb");

    fwrite(data, sizeof(unsigned char), n, fp);

    fclose(fp);
}

在我的机器上的调试版本中,程序在大约1分钟内完成(malloc只需几秒钟,因此这大部分是fwrite),平均消耗30%的CPU。 PerfMon显示完全在最后发生的写入是4 GB(写缓存)的单个“闪存”。

在n的分配中将- 1更改为+ 1并重现问题:瞬时100%的CPU使用率并且没有写入任何内容。几分钟后,文件的大小仍然是0字节(在我的实际代码中调用,它设法转储70 MB左右)。

这绝对是fwrite中的问题,因为以下代码可以正常编写文件:

int main()
{
    FILE *fp;
    long long n;
    long long counter = 0;
    long long chunk;
    unsigned char *data;

    n = 4LL * 1024 * 1024 * 1024 + 1;

    data = (unsigned char *)malloc(n * sizeof(unsigned char));

    fp = fopen("T:\\test.bin", "wb");

    while (counter < n)
    {
        chunk = min(n - counter, 100*1000);
        fwrite(data+counter, sizeof(unsigned char), chunk, fp);
        counter += chunk;
    }

    fclose(fp);
}

在我的机器上,这需要45秒而不是1分钟。 CPU使用率不是常数,它是突发性的,报告的IO写入比“单块”方法更分散。

如果速度的增加是错误的(即缓存),我会感到非常惊讶,因为我在编写包含所有相同数据的多个文件与包含随机数据的文件和报告的写入速度之前已经完成了测试(缓存)是一样的。所以我愿意打赌,至少fwrite的这种实现并不喜欢一次传递给它的巨大块。

我还在关闭文件后立即读取fread以便在4 GB + 1的情况下写入并及时返回 - 最多几秒钟(这里没有真实数据所以我没有检查一下。)

修改

我使用chunk-writing方法和4 GB-1文件的单个fwrite调用(两种方法都可以做到的最大大小)运行一些测试。多次运行程序(使用代码打开文件,使用多个fwrite调用写入,关闭,然后再次打开,写入单个调用并关闭),毫无疑问,块写入方法返回的速度更快。在最坏的情况下,它会在68%的单次通话时间内返回,最多只有20%。

答案 1 :(得分:0)

这不是带有fwrite问题,但有意(虽然不可否认是 uncool )行为:

  

fwrite()函数应从ptr指向的数组写入最大nitems个大小由size指定的元素到由流。 对于每个对象,应对size函数进行fputc()次调用,从数组中获取值(按顺序)<...]

所以基本上,通过正确使用fwrite 而不作弊,您需要拨打数十亿次来fputc的电话。
考虑到上述要求,很明显你必须作弊才能使其正常运作。