How to obtain good concurrent read performance from disk我想问一个问题,然后用我自己的答案跟进,但还要看看其他人有什么答案。 我们有两个大文件,希望同时从两个单独的线程中读取。一个线程将顺序读取fileA,而另一个线程将顺序读取fileB。线程之间没有锁定或通信,它们都以尽可能快的速度顺序读取,并且都立即丢弃读取的数据。 我们在Windows上使用此设置的经验非常差。两个线程的合并吞吐量约为2-3 MiB / sec。该驱动器似乎在大部分时间上都在两个文件之间来回搜索,大概每次搜索后读取的内容很少。 如果我们禁用其中一个线程并暂时查看单个线程的性能,那么我们将获得更好的带宽(这台机器约为45 MiB /秒)。因此很明显,糟糕的两线程性能是OS磁盘调度程序的假象。 有什么办法可以改善并发线程的读取性能?也许通过使用不同的API或通过某种方式调整OS磁盘调度程序参数。 一些细节: 在具有2GiB RAM的计算机上,每个文件的顺序为2 GiB。出于这个问题的目的,我们认为它们不会被缓存和完美地进行碎片整理。我们使用了碎片整理工具并重新启动以确保是这种情况。 我们没有使用特殊的API来读取这些文件。该行为在各种沼泽标准API(例如Win32的CreateFile,C的fopen,C ++的std :: ifstream,Java的FileInputStream等)中都是可重复的。 每个线程旋转一个循环,以调用read函数。我们将每次迭代从API请求的字节数从1KiB到128MiB之间的值进行了更改。改变它没有任何作用,因此很明显,每个磁盘搜索之后操作系统的物理读取量不受此数字的限制。这正是应该期望的。 在Windows 2000,Windows XP(32位和64位),Windows Server 2003以及带有或不带有硬件RAID5的情况下,单线程和双线程性能之间的巨大差异是可重复的。
问题似乎出在Windows I / O调度策略中。根据我在这里找到的信息,有多种方法可以进行O.S.安排磁盘请求。尽管Linux和其他操作系统可以在不同的策略之间进行选择,但在Vista Windows被锁定为单个策略之前:FIFO队列,其中所有请求均分成64 KB的块。我认为,此策略是导致您遇到问题的原因:调度程序将混合来自两个线程的请求,从而导致在磁盘的不同区域之间进行连续查找。
您可以使用信号量进行状态检查,也可以使用perfmon计数器获取实际磁盘队列的状态。 我想在回复中添加一些其他说明。我们测试过的所有其他非Microsoft操作系统都不会遇到此问题。从一个线程转移到两个线程时,Linux,FreeBSD和Mac OS X(最后一个版本在不同的硬件上)在聚合带宽方面的降幅要大得多。例如,Linux从?45 MiB / sec降级到?42 MiB / sec。这些其他操作系统必须在每次搜索之间读取文件的更大块,因此,几乎不会花费所有时间在磁盘上等待搜索。
我们针对Windows的解决方案是将
(最后一点。这是否解释了为什么在Windows下进行交换如此令人讨厌?即Windows无法以任何效率同时对多个文件执行IO,因此在交换所有其他IO操作时被迫变得异常缓慢。) 编辑以添加有关Will Dean的更多详细信息: 当然,在这些不同的硬件配置中,原始数据确实发生了变化(有时会发生很大变化)。但是,问题是从一个线程转移到两个线程时,只有Windows会遭受性能的持续下降。以下是测试机器的摘要:
在测试过程中,所有这些系统上的CPU使用率都很低,并且禁用了防病毒功能。
我之前忘记提及,但我们也尝试了设置 您在相当广泛的Windows版本中看不到任何区别,并且在单个驱动器和硬件raid-5之间没有任何区别,这似乎有些奇怪。 只是"胆量",但这确实使我怀疑这确实是一个简单的寻求问题。除了OS X和Raid5,所有这些都在同一台机器上尝试过-您是否尝试过另一台机器?在此测试期间,您的CPU使用率是否基本为零? 您能写出的最短的应用程序证明了这个问题? -我想在这里尝试一下。 保罗-看到了更新。很有意思。 在Vista或Win2008上试用它会很有趣,因为人们似乎在某些情况下报告了这些方面的相当大的I / O改进。 我对不同API的唯一建议是尝试使用内存映射文件-您是否尝试过?不幸的是,每个文件只有2GB,您将无法在32位计算机上映射多个完整文件,这意味着它并不是那么简单。 您是否在Windows下使用IOCompletionPorts?通过C ++的Windows对此主题有深入的介绍,很幸运,它也可以在MSDN上获得。 我会在内存线程中创建某种安全锁。每个线程可以等待锁定,直到释放为止。锁定释放后,获取锁定并在规定的时间长度或定义的数据量内读取文件,然后为其他等待线程释放锁定。 |