Windows 如何从磁盘获得良好的并发读取性能

Windows 如何从磁盘获得良好的并发读取性能,windows,multithreading,file-io,Windows,Multithreading,File Io,我想问一个问题,然后用我自己的答案跟进,但也看看其他人有什么答案 我们有两个大文件,希望同时从两个单独的线程读取。一个线程将顺序读取文件A,而另一个线程将顺序读取文件B。线程之间没有锁定或通信,两个线程都以尽可能快的速度顺序读取数据,并且都立即丢弃它们读取的数据 我们在Windows上进行此设置的经验非常差。两个线程的总吞吐量约为2-3 MiB/秒。驱动器似乎花费了大部分时间在两个文件之间来回搜索,可能每次搜索后读取很少 如果我们禁用其中一个线程并临时查看单个线程的性能,那么我们可以获得更好的带

我想问一个问题,然后用我自己的答案跟进,但也看看其他人有什么答案

我们有两个大文件,希望同时从两个单独的线程读取。一个线程将顺序读取文件A,而另一个线程将顺序读取文件B。线程之间没有锁定或通信,两个线程都以尽可能快的速度顺序读取数据,并且都立即丢弃它们读取的数据

我们在Windows上进行此设置的经验非常差。两个线程的总吞吐量约为2-3 MiB/秒。驱动器似乎花费了大部分时间在两个文件之间来回搜索,可能每次搜索后读取很少

如果我们禁用其中一个线程并临时查看单个线程的性能,那么我们可以获得更好的带宽(对于这台机器,大约45 MiB/秒)。显然,糟糕的双线程性能是OS磁盘调度程序的产物

我们能做些什么来提高并发线程读取性能吗?也许可以通过使用不同的API或以某种方式调整操作系统磁盘调度程序参数

一些细节:

在具有2GiB RAM的机器上,每个文件的顺序为2 GiB。为了这个问题的目的,我们认为它们不应该被缓存和完全碎片化。我们使用了碎片整理工具并重新启动,以确保情况属实

我们没有使用特殊的API来读取这些文件。这种行为可以在各种bog标准API中重复,如Win32的CreateFile、C的fopen、C++的std::ifstream、Java的FileInputStream等

每个线程在一个循环中旋转,调用read函数。我们已经改变了每次迭代从API请求的字节数,从1KB到128MiB。改变这一点没有任何效果,因此很明显,操作系统在每次磁盘寻道后的物理读取量并不是由这个数字决定的。这正是我们应该期待的


单线程和双线程性能之间的巨大差异可以在Windows 2000、Windows XP(32位和64位)、Windows Server 2003以及有无硬件RAID5之间重复出现。

我想在我的回答中添加一些进一步的说明。我们测试过的所有其他非Microsoft操作系统都不会遇到此问题。Linux、FreeBSD和Mac OS X(这是不同硬件上的最后一个)在从一个线程移动到两个线程时,在聚合带宽方面都会有更大的降低。例如,Linux从每秒45兆字节降级到每秒42兆字节。这些其他操作系统必须在每次查找之间读取更大的文件块,因此不会花费几乎所有的时间在磁盘上等待查找

我们针对Windows的解决方案是将
FILE\u标志\u NO\u BUFFERING
标志传递给
CreateFile
,并在每次调用
ReadFile
时使用大(~16MiB)读取。这是不理想的,原因如下:

  • 这样读取文件时不会缓存文件,因此缓存通常没有任何优势
  • 使用此标志时的约束比正常读取(读取缓冲区与页面边界对齐等)复杂得多
(作为最后一句话。这是否解释了为什么在Windows下进行交换如此糟糕?即,Windows无法以任何效率同时对多个文件执行IO,因此在交换所有其他IO操作时,速度会异常缓慢。)


编辑以添加Will Dean的更多详细信息:

当然,在这些不同的硬件配置中,原始数据确实发生了变化(有时变化很大)。然而,问题在于,当从一个线程移动到两个线程时,只有Windows的性能会持续下降。以下是已测试机器的摘要:

  • 多个不同年龄段的Dell工作站(Intel Xeon)使用单驱动器运行Windows 2000、Windows XP(32位)和Windows XP(64位)
  • 运行带有RAID 1+0的Windows server 2003(64位)的Dell 1U服务器(Intel Xeon)
  • 配备Windows XP(64位)、Windows Server 2003和硬件RAID 5的HP工作站(AMD Opteron)
  • 我的家用无品牌PC(AMD Athlon64),运行Windows XP(32位)、FreeBSD(64位)和Linux(64位),单驱动器
  • 我家的MacBook(Intel Core1)运行Mac OS X,单SATA驱动器
  • 我的家用电脑运行Linux。与其他系统相比,它的动力大大不足,但我已经证明,即使是这台机器,在进行多线程磁盘读取时,其性能也会优于使用RAID5的Windows服务器
在测试期间,所有这些系统上的CPU使用率都非常低,并且防病毒功能被禁用


我之前忘了提到,但我们也尝试了正常的Win32
CreateFile
API,设置了
FILE\u标志\u SEQUENTIAL\u SCAN
标志。此标志没有解决问题。

我将创建某种内存中的线程安全锁。每个线程都可以等待锁释放。当锁释放时,获取锁并读取文件定义的时间长度或定义的数据量,然后释放任何其他等待线程的锁。

在相当广泛的windows版本中看不到任何差异,在单个驱动器和硬件raid-5之间看不到任何差异,这确实有点奇怪

这只是“直觉”,但这确实让我怀疑这是否真的是一个简单的问题。除了OSX和Raid5,所有这些都在同一台机器上试过了吗?你试过其他机器吗?在这个测试中,您的CPU使用率基本为零吗

你能写的最短的应用程序是什么,它演示了这个问题我有兴趣在这里试用。

您在Windows下使用吗?Windows通过C++深入讨论了这个问题,幸运的是,

P
if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again