【问题标题】:Multithreading a File Map into an Array of Buffers将文件映射多线程化到缓冲区数组中
【发布时间】:2013-03-12 16:02:35
【问题描述】:

我正在尝试处理令人讨厌的大型 xml 和文本文档:~40GBs。 我在 Windows 7 上使用 Visual Studio 2012。

我将使用“Xerces”从 xml 中获取页眉/“页脚标签”。

我想映射文件的一个区域,比如说.. 60-120MB。

将地图分成(3 * 个处理器/内核)相等的部分。将每个部分设置为缓冲区并将缓冲区加载到数组中。

然后在新线程中使用 (#processors/cores) while statments,我将在浏览缓冲区数组时同步计算字符/行/xml 周期。当一个缓冲区完成时,该进程将跳转到下一个“可用”缓冲区,并且完成的缓冲区将从内存中删除。最后,我会将总结果添加到项目日志中。

之后,我将参考日志,按字符数/大小(或其他选项)将文件拆分到最近的行或循环,然后将页眉和“页脚标签”放入所有拆分中。

我这样做是为了可以通过具有多台计算机的网络将大量数据导入 MySQL 服务器。

我的问题是,如何使用新线程创建缓冲区数组和文件映射?

我可以使用吗:

赢得创建文件

赢得创建文件映射

赢得 MapViewOfFile

使用标准 ifstream 操作和字符缓冲区,还是应该选择其他方式?

进一步说明: 我的想法是,如果我可以让硬盘驱动器将文件从一个位置和一个方向流式传输到内存中,我就可以利用机器的全部处理能力来咀嚼单独但相等的缓冲区。

~味道:这有点像一个牧羊人试图从一个带有 3-6 个大桶的大垃圾箱中舀出食物,只有两条手臂用于需要留在围栏区域内的 X 只羊。但它们都以光速运动。

这里有一些想法或建议可能会对我有所帮助。 任何想法都是最受欢迎的。谢谢。

while(getline(my_file, myStr))
{
   characterCount += myStr.length();

   lineCount++;


   if(my_file.eof()){

      break;

   }
}

这是测试运行时唯一的代码。 2小时30+分钟。在具有 2GB RAM 的双核 1.6Mhz 笔记本电脑上运行程序的总处理器为 45-50%。现在加载的大部分 RAM 是 600+MB,在 Firefox 中打开约 50 个选项卡,Visual Studio 为 60MB,然后等等。

重要提示:在测试过程中,运行代码的程序(只有一个窗口和一个对话框)似乎转储了它自己的工作和私有内存集,降至 300K 左右,并且没有响应测试的长度。我确定我需要为 while 语句创建另一个线程。但这意味着没有文件被读入缓冲区。 CPU 在整个运行过程中都在努力跟上硬盘驱动器的最小努力。

附: CPU 瓶颈的进一步证明。通过我的无线网络将整个文件传输到另一台计算机可能需要 20 分钟。其中包括读取进程和套接字捕获以在另一台计算机上写入进程。

更新

我用这个可爱的小东西从之前的测试时间缩短到大约 15-20 分钟,这与 Mats Petersson 所说的一致。

while (my_file.read( &bufferOne[0], bufferOne.size() ))

{

int cc = my_file.gcount();

for (int i = 0; i < cc; i++)
{

    if (bufferOne[i] == '\n')
        lineCount++;

    characterCount++;

}

currentPercent = characterCount/onePercent;

SendMessage(GetDlgItem(hDlg, IDC_GENPROGRESS), PBM_SETPOS, currentPercent, 0);

}

当然,这是一个单循环,它实际上比之前的测试表现得更合适。该测试比上面显示的使用 Getline 的紧密循环快约 800%。我将此循环的缓冲区设置为 20MB。我从:SOF - Fastest Example

中提取了此代码

但是...

我想指出的是,在资源监视器和任务管理器中轮询进程时,它清楚地显示第一个内核的使用率为 75-90%,第二个内核的使用率为 25-50%(对于一些较小的背景内容来说,这是相当标准的我已经打开),硬盘在..等待它... 50%。一些 100% 的磁盘时间峰值,但也有一些 25% 的低点。所有这些基本上意味着在两个不同的线程之间拆分缓冲区处理很可能是一个好处。它将使用所有系统资源,但是..这就是我想要的。当我有工作原型时,我会在今天晚些时候更新。

主要更新: 经过一番学习,终于完成了我的项目。不需要文件映射。只有一堆矢量字符。我已经成功构建了一个动态执行的文件流线和字符计数器。 好消息,从之前的 10-15 分钟标记到 5.8GB 文件的约 3-4 分钟,BOOYA!~

【问题讨论】:

  • 这不是 SAX 设计的线性解析吗?我不认为它会允许多台机器这样做。
  • 之后我使用多台机器进行导入过程。文件的解析和分割只能在一个系统上完成。
  • 好的,所以第一件相当明显的事情是,一个 5.6GB 的文件可能包含大约 100M 的换行符,所以这是 100M 的除法操作(大约 20-30 个时钟周期)和 100M 的调用SendMessage() 和 GetDlgItem()。这两者都可能比字符串的输入读数更重。添加类似if ((lineCount &amp; 1024) == 0) { currentPercent = ....; SendMessage(...); }
  • 不确定您是否在下面看到我的评论,但在构建/运行测试之后添加了 SendMessage 和当前百分比位。它只是带有字符数、行数和 break 语句的 while 语句。
  • 不过你的权利。我不需要它发送 1 亿个 SendMessage 请求,但确实可以顺利过渡;)

标签: c++ multithreading winapi buffer


【解决方案1】:

非常简短的回答:是的,您可以使用这些功能。

对于读取数据,将文件内容映射到内存中可能是最有效的方法,因为它不必将内存复制到应用程序的缓冲区中,只需将其直接读取到它应该去的地方。所以,只要你有足够的可用地址空间就没有问题 - 64 位机器当然应该有足够的,在 32 位系统中它可能是更稀缺的资源 - 但对于几百 MB 的部分,它应该这不是一个大问题。

但是,使用多个线程,我一点也不相信。我有一个公平的想法,即读取一个非常大的文件的多个部分会适得其反。这将增加磁盘上的磁头移动量,这是传输速率的很大一部分。对于“普通”系统,您可以指望大约 50-100MB/s 的传输速率。如果系统有某种 raid 控制器或类似的,可能会翻倍——非常奇特的raid 控制器可能会达到三倍。

因此读取 40GB 大约需要 3-15 分钟。

CPU 可能不会很忙,运行多个线程很可能会降低系统的整体性能。

您可能希望保留一个用于读取的线程和一个用于写入的线程,并且仅在您拥有足够数量的数据时才实际写出数据,再次,以避免磁盘上的读/写头不必要的移动(s )。

【讨论】:

  • 这或多或少是我的想法,只是我想让处理器工作得更多。当我做这些操作时,硬盘驱动器似乎是最快的。在操作完成之前,我不会将数据写入日志,但我正在考虑使用多个线程,这样我就可以最大限度地使用处理器。我还想要单独的线程,因为我每 30 秒轮询一次使用统计信息。我想让线程数动态化,以便系统在任何区域都尽可能接近 100%。
  • 无论如何都要试试这个——创建一个大文件,从不同线程中的不同部分读取[使其可配置它有多少线程],然后花时间。但我预计吞吐量会更低。
  • 嗯,这就是为什么我要尝试获取一个 120MB 的块并将卡盘分成几块以从内存中读取,这样,处理器或多或少会保持同步它将始终从内存中相同的 120MB 块中读取。从磁盘的角度来看,120MB/6 = 6 * 20MB 内存中的缓冲区都来自文件的同一区域。然后,当一个 20MB 完成后,磁盘将继续从它离开的同一位置读取另一个 20MB。
  • 我可能误解了您对硬盘驱动器功能的了解。在这种情况下,我将不得不重新考虑并简化为一个“while”语句。但是,如果您有任何其他想法或 cmets,我很想听听。
  • 是的,但是现代处理器处理 20MB 所需的时间比硬盘传输下一个 20MB 所需的时间要短得多。但是,读取 SMALL 部分实际上比读取大部分要快 - 但是对于内存映射文件,这不是一个小问题,因为操作系统会在需要时以(通常)4KB 的块读取文件的位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2014-12-28
  • 1970-01-01
相关资源
最近更新 更多