超过2.5GB写入后,使用fputs的性能下降。为什么?

8

目前我正在开发一个读取大文件并对其进行排序的小程序。在进行一些基准测试后,我遇到了一个奇怪的性能问题。当输入文件变得很大时,输出文件的写入时间比实际排序时间更长。因此,我深入研究了代码,并最终意识到fputs函数可能是问题所在。于是我编写了这个小型基准测试程序。

#include "stdio.h"
#include "ctime"

int main()
{
    int i;
    const int linecount = 50000000;
    //Test Line with 184 byte
    const char* dummyline = "THIS IS A LONG TEST LINE JUST TO SHOW THAT THE WRITER IS GUILTY OF GETTING SLOW AFTER A CERTAIN AMOUNT OF DATA THAT HAS BEEN WRITTEN. hkgjhkdsfjhgk jhksjdhfkjh skdjfhk jshdkfjhksjdhf\r\n";
    clock_t start = clock();
    clock_t last = start;

    FILE* fp1 = fopen("D:\\largeTestFile.txt", "w");
    for(i=0; i<linecount; i++){
        fputs(dummyline, fp1);
        if(i%100000==0){
            printf("%i Lines written.\r", i);
            if(i%1000000 == 0){
                clock_t ms = clock()-last;
                printf("Writting of %i Lines took %i ms\n", i, ms);
                last = clock();
            }
        }
    }
    printf("%i Lines written.\n", i);
    fclose(fp1);
    clock_t ms = clock()-start;
    printf("Writting of %i Lines took %i ms\n", i, ms);

}

当您执行程序时,可以看到大约在1400万至1500万行左右(约为2.5GB的数据)后性能明显下降。写入所需时间是之前的3倍。2GB阈值表明存在64位问题,但我在网络上没有找到任何相关信息。我还测试了二进制和字符模式之间是否有区别(例如“wb”和“w”),但并没有发现差异。我还尝试通过将文件大小预分配(以避免文件碎片化),将指针定位于期望的结尾并写入一个零字节,但效果很小或没有效果。
我正在运行Windows 7 64位机器,但我也在Windows Server 2008 64位 R1机器上进行了测试。目前,在具有超过200GB可用空间的NTFS文件系统上进行测试。我的系统有16GB RAM,因此也不应该存在问题。测试程序仅使用约700KB。我先前怀疑的页面错误数量也非常低(整个运行时间中约400页错误)。
我知道对于如此大的数据,fwrite()函数可能更适合此任务,但目前我想知道是否有其他解决方法以及为什么会出现这种情况。任何帮助都将不胜感激。

4
你正在写入哪个文件系统?你运行这段代码的设备有多少内存? - m0ntassar
正如我之前添加的,文件系统是NTFS。但我真的很想知道该代码在其他文件系统/操作系统上是否存在相同的问题。 - Aceonline
你在运行测试之前是否对磁盘进行了碎片整理?顺便问一下:Windows 上是否有类似于 /dev/null 的东西? - alk
是的,碎片化不应该成为瓶颈。 - Aceonline
1个回答

9
主要原因是Windows磁盘缓存。然后你的程序使用所有可用的RAM,接着开始交换内存,从而导致减速。为了解决这些问题,你需要:
1)使用“c”标志以提交模式打开文件:
FILE* fp1 = fopen("D:\\largeTestFile.txt", "wc");

2) 使用flush函数定期将缓冲区写入磁盘:

if(i%1000000 == 0)
{
    // write content to disk
    fflush(fp1);

    clock_t ms = clock()-last;
    printf("Writting of %i Lines took %i ms\n", i, ms);
    last = clock();
}

这样,您将使用适量的磁盘缓存。速度基本上受限于硬盘的速度。


性能并不差,它受到硬盘速度的限制。在您的原始源代码中,您的数据实际上从未写入文件(因此最终的最后一次写入将需要相当长的时间)。您的“前14 Mio行”的速度也受到硬盘速度的限制,因为使用了交换。 - Petr Abdulin
好的,我明白你的意思。但事实是,在我加入fflush之前它花费了100秒,现在需要156秒。 - Aceonline
增加更多的RAM会有帮助吗?因为缓存可以变得更大?或者缓存被限制在2G字节内? - alk
@user1039287 我在 Server 2008 R2 x64 (6 Gb RAM) 上测试了你的程序,发现出现了相同的症状(唯一的区别是开始减速的数字不同),因此服务器也无法避免这个问题。我还监测了内存使用情况,这让我得出了答案。SSD 必须更快,所以问题不是那么明显。 - Petr Abdulin
我已经尝试使用AnalogX CacheBooster工具将磁盘缓存增加到最大值,但似乎存在2GB的限制(至少在该工具中是如此)。增加到2GB也没有改变性能。这似乎与我的经验相符,即2.5GB的阈值(约500MB在填充缓存时写入)。但我在网上没有找到关于Windows磁盘缓存2GB限制的任何信息。奇怪的oO。 - Aceonline
显示剩余6条评论

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