为什么C++的fseek/fread性能比C#的FileStream的Seek/Read高几倍

5
我正在进行一个相当简单的测试:
  1. 有一个随机二进制信息的大文件,大小约为6Gb
  2. 算法执行“SeekCount”次循环
  3. 每次重复执行以下操作:
    • 计算文件大小范围内的随机偏移量
    • 跳转到该偏移量
    • 读取小块数据

C#:

    public static void Test()
    {
        string fileName = @"c:\Test\big_data.dat";
        int NumberOfSeeks = 1000;
        int MaxNumberOfBytes = 1;
        long fileLength = new FileInfo(fileName).Length;
        FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536, FileOptions.RandomAccess);
        Console.WriteLine("Processing file \"{0}\"", fileName);
        Random random = new Random();
        DateTime start = DateTime.Now;
        byte[] byteArray = new byte[MaxNumberOfBytes];

        for (int index = 0; index < NumberOfSeeks; ++index)
        {
            long offset = (long)(random.NextDouble() * (fileLength - MaxNumberOfBytes - 2));
            stream.Seek(offset, SeekOrigin.Begin);
            stream.Read(byteArray, 0, MaxNumberOfBytes);
        }

        Console.WriteLine(
            "Total processing time time {0} ms, speed {1} seeks/sec\r\n",
            DateTime.Now.Subtract(start).TotalMilliseconds, NumberOfSeeks / (DateTime.Now.Subtract(start).TotalMilliseconds / 1000.0));

        stream.Close();
    }

然后在C++中进行相同的测试:

void test()
{
     FILE* file = fopen("c:\\Test\\big_data.dat", "rb");

char buf = 0;
__int64 fileSize = 6216672671;//ftell(file);
__int64 pos;

DWORD dwStart = GetTickCount();
for (int i = 0; i < kTimes; ++i)
{
    pos = (rand() % 100) * 0.01 * fileSize;
    _fseeki64(file, pos, SEEK_SET);
    fread((void*)&buf, 1 , 1,file);
}
DWORD dwEnd = GetTickCount() - dwStart;
printf(" - Raw Reading: %d times reading took %d ticks, e.g %d sec. Speed: %d items/sec\n", kTimes, dwEnd, dwEnd / CLOCKS_PER_SEC, kTimes / (dwEnd / CLOCKS_PER_SEC));
fclose(file);
}

执行时间:

  1. C#: 100-200次读取/秒
  2. C++: 250,000次读取/秒(250,000次)

问题:为什么在像文件读取这样的琐碎操作上,C++比C#快数千倍?

额外信息:

  1. 我玩过流缓冲区并将它们设置为相同的大小(4 KB)。
  2. 硬盘是碎片化的(0%碎片化)。
  3. 操作系统配置:Windows 7,NTFS,一些最新的现代500GB硬盘(如果记得正确的话是WD),8 GB RAM(虽然几乎没有使用),4核CPU(利用率几乎为零)。

5
那不是C++,而是C语言。 - Some programmer dude
1
另外,MaxNumberOfBytes的值是多少? - Some programmer dude
5
"(rand()%100)*0.01"似乎有些可疑 - 实际上你只读取文件的100个偏移量中的一个,这可能利用底层缓存机制,因此并没有真正从磁盘中读取块。尝试使用类似于C#版本的代码"rand() % fileSize",以使代码更加一致。 - andr
1
根据您实际想要做什么,您可能需要研究一下内存映射文件 - Some programmer dude
2
@David:你没有理解他的观点,C++代码只会从随机选择的100个位置读取,而Java将从所有位置读取,这几乎不公平。对于C++,将其更改为rand()/double(RAND_MAX)*fileSize以具有与Java相同的功能。 - Mooing Duck
显示剩余12条评论
1个回答

6

C++版本的测试存在错误——随机偏移量的计算被限制了,因此只在短距离内进行了查找,这使得C++的结果看起来更好。

@MooingDuck提出了正确的偏移量计算代码:

rand()/double(RAND_MAX)*fileSize

通过这个修改,C++和C#的性能变得可比较——大约为每秒200次读取。

感谢所有人的贡献。


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