【问题标题】:How to increase speed of reading data on Windows using c++如何使用 C++ 在 Windows 上提高读取数据的速度
【发布时间】:2014-07-16 06:32:31
【问题描述】:

我正在使用CreateFile/ReadFile 和 4096 字节的缓冲区大小从卷快照中读取数据块。 我面临的问题是ReadFile 太慢了,我可以在 45 秒内读取 68439 个块,即 267 Mb,如何提高速度?以下是我正在使用的代码的一部分,

block_handle = CreateFile(block_file,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if(block_handle != INVALID_HANDLE_VALUE)
{
    DWORD pos = -1;
    for(ULONG i = 0; i < 68439; i++)
    {
        sectorno = (i*8);
        distance = sectorno * sectorsize;
        phyoff.QuadPart = distance;     
        if(pos != phyoff.u.LowPart)
        {
             pos=SetFilePointer(block_handle, phyoff.u.LowPart,&phyoff.u.HighPart,FILE_BEGIN);
             if (phyoff.u.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
             {
                 printf("SetFilePointer Error: %d\n", GetLastError());
                 phyoff.QuadPart = -1;
                 return;
             }
        }
        ret = ReadFile(block_handle, data, 4096, &dwRead, 0);
        if(ret == FALSE)
        {
            printf("Error Read");
            return;
        }
        pos += 4096;
    }
}

我应该使用OVERLAPPED 结构吗?或者可能的解决方案是什么? 注意:代码没有线程化。

等待积极的回应。

【问题讨论】:

  • 我看到循环目前基本上什么都不做 - 您读取的数据没有用于任何事情。这使我认为您省略了可能对答案很重要的代码。我的直接想法是(a)你的分析器告诉你什么? (b) 这与仅分析发布的代码时的结果有显着不同吗? (c) 那么内存映射文件访问呢,你调查过吗?最后,这段代码中sectorSize 的典型值是什么?
  • @enhzflep:是的,我省略了其他代码,它只是将数据转储到另一个卷中。但我需要先测试速度,在我的实际代码中,我也注释了这些行并只测试了上面的代码。我没有尝试过内存映射文件访问,因为我不知道文件名,所以从块/集群中读取数据是否有用。扇区大小为 512。
  • 我仍然想知道为什么您似乎没有通过分析器运行代码!? (mingw & VS 自带)这将告诉你代码的哪一部分出现了瓶颈。 - 不确定内存映射文件的来龙去脉,快速检查 MSDN 表明CreateFileMapping 采用HANDLE,这是从对CreateFile 的调用中返回的。这些可能会有所帮助:1 msdn.microsoft.com/en-au/library/ms810613.aspx 2 msdn.microsoft.com/en-us/library/windows/desktop/… 3 msdn.microsoft.com/en-us/library/windows/desktop/…
  • 对于扇区大小-您的示例显示了数据的顺序访问。如果(读取的最后位置 + 4096 字节)==(读取的新位置),则执行查找操作。

标签: c++ windows performance file-io block


【解决方案1】:

我不太确定您为什么要为此使用这些极低级别的系统函数。

我个人使用 C 风格的文件操作(使用 fopen 和 fread)以及 C++ 风格的操作(使用 fstream 和 read,参见 this link)来读取原始二进制文件。从本地磁盘读取速度约为 100MB/秒。

在您的情况下,如果您不想使用标准的 C 或 C++ 文件操作,我的猜测是您的代码较慢的原因是您在每个块之后执行查找。你真的需要为每个块调用 SetFilePointer 吗?如果块是连续的,则不需要这样做。

另外,尝试不同的块大小,不要害怕超过 1MB。

【讨论】:

  • 感谢您的回复。实际上我只知道块号,这就是我使用低级系统函数的原因,我不知道文件名。如果我的块是连续的并且如果我不为每个块调用SetFilePointer,那么ReadFile 函数将如何知道从哪里读取?您能否用与SetFilePointer 相关的短代码或示例进行解释?而且我一次只需要读取4096字节的数据,所以目前无法增加blocksize。
  • 在我拥有和拥有的不同计算机中,我总是能以最快的速度读取 64k 块。另外,正如@CaptainCodeman 所说,尽量避免搜索操作。
  • 另外告诉你,这些块可能是连续的,但不是必须的,我需要阅读所有的块。例如:阅读第 1000 个块后,我可能正在阅读第 1500 个块并跳过 1000-1500 之间的块。
  • @MauroH.Leggieri:这就是我要问的问题,我怎样才能避免查找操作,因为我不知道列表中的下一个块号,正如我在之前的评论中已经提到的那样。您能指导我参考我可以参考的示例或代码吗?我也知道增加块大小可能会提高速度,但我只能读取 4096 字节的数据,不能增加它。
  • 如果我理解正确,您是否尝试直接从文件系统读取并从中提取文件等?数据是否分散在卷周围的 4K 块中,您需要读取一个以找出下一个在哪里?如果是这种情况,您的数据是零散的,并且确实没有一种快速的方法来读取零散的数据。 (这就是人们对文件系统进行碎片整理的原因。)如果您的数据大部分是连续的,ReadFile 会更新文件指针,这样您就不需要读取下一个块。
【解决方案2】:

您的问题是碎片数据读取。您无法通过摆弄 ReadFile 参数来解决此问题。您需要对读取进行碎片整理。以下是三种方法:

  1. 对磁盘上的数据进行碎片整理

  2. 对读取进行碎片整理。也就是说,收集您需要的所有阅读内容,但不要阅读任何内容。将读取排序。按顺序阅读所有内容,尽可能跳过 SetFilePointer(即顺序块)。这将大大加快总读取速度,但会在第一次读取开始之前引入延迟。

  3. 内存映射数据。将所有数据复制到内存中并从内存中进行随机访问读取。这是否可能取决于总共有多少数据。

此外,您可能想花点心思尝试缓存。当你读取一个数据块时,可能是下一次读取虽然不是顺序的,但很可能是接近的概率很高。因此,当您读取一个块时,将附近的一大块数据顺序读取到内存中。在下一次读取之前,检查新读取是否已经在内存中 - 从而节省了查找和磁盘访问。测试、调试和调整这是一项大量工作,所以我不推荐它,除非这是一项关键任务优化。另请注意,您的操作系统和/或您的磁盘硬件可能已经在执行这些操作,因此请准备好看到没有任何改进。

【讨论】:

  • 感谢您的回复。你的第一种方法对我和第三种方法都不起作用。由于分区可以是任何大小,可能是 10gb 或 100gb 或 1tb,所以第三种方法不起作用。我将尝试第二种方法,并再次通知您。
  • 你所说的“尝试缓存并顺序读取附近的一大块数据”是什么意思,你的意思是说存储新的块位置或将下一个数据块复制到内存中?顺便说一句,我尝试存储下一个块位置并将其与新块位置进行比较,如果相同,则避免查找操作,但没有帮助,速度仍然相同。我已经编辑了我的主要问题并添加了新代码,请看一下。
【解决方案3】:
  1. 如果可能,请按顺序读取(并告诉 CreateFile 您打算使用FILE_FLAG_SEQUENTIAL_SCAN 按顺序读取)。
  2. 避免不必要的搜索。如果您是按顺序阅读,则不需要任何搜索。
  3. 读取更大的块(如典型集群大小的整数倍)。我相信 Windows 自己的文件副本使用 8 MB 而不是 4 KB 的读取量。考虑使用系统分配粒度的整数倍(可从 GetSystemInfo 获得)。
  4. 从对齐的偏移量读取(您似乎正在这样做)。
  5. 读取到页面对齐的缓冲区。考虑使用 VirtualAlloc 来分配缓冲区。
  6. 请注意,文件碎片可能会导致昂贵的查找。对此您无能为力。
  7. 请注意,卷压缩会使搜索变得特别昂贵,因为它可能必须从头开始解压缩文件才能找到文件中间的起点。
  8. 请注意,卷加密可能会减慢速度。您无能为力,但请注意。
  9. 请注意,其他软件(如反恶意软件)可能会在您每次触摸时扫描整个文件。更少的操作将减少这种影响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-15
    • 2019-06-22
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    相关资源
    最近更新 更多