Dam*_*mon 18 windows winapi buffered overlapped-io
(道歉有些冗长的介绍)
在此期间,prefaults整个大文件(> 400MB)到缓冲区缓存后加快了实际运行的应用程序的开发,我测试是否每次读4MB仍然有超过一次读取只有1MB块任何明显的好处.令人惊讶的是,较小的请求实际上变得更快.这似乎违反直觉,所以我进行了更广泛的测试.
缓冲区缓存在运行测试之前被清除(只是为了笑,我也在缓冲区中运行了一个文件.无论请求大小如何,缓冲区缓存都能提供高达2GB/s的速度,但令人惊讶的是+/- 30%随机方差).
使用的所有读取都与相同的目标缓冲区重叠ReadFile(使用FILE_FLAG_OVERLAPPED和不 使用句柄打开FILE_FLAG_NO_BUFFERING).使用的硬盘有点老,但功能齐全,NTFS的簇大小为8kB.初始运行后磁盘进行了碎片整理(6个碎片与未碎片,零差异).为了更好的数字,我也使用了更大的文件,下面的数字是读取1GB.
结果真的令人惊讶:
4MB x 256 : 5ms per request, completion 25.8s @ ~40 MB/s
1MB x 1024 : 11.7ms per request, completion 23.3s @ ~43 MB/s
32kB x 32768 : 12.6ms per request, completion 15.5s @ ~66 MB/s
16kB x 65536 : 12.8ms per request, completion 13.5s @ ~75 MB/s
Run Code Online (Sandbox Code Playgroud)
因此,这表明提交数千个请求两个簇的长度实际上比提交几百个大的连续读取更好.提交时间(ReadFile返回之前的时间)确实随着请求数量的增加而上升,但异步完成时间几乎减半.
在每种情况下,内核CPU时间大约为5-6%(在四核上,所以应该说20-30%),而异步读取正在完成,这是一个惊人的CPU数量 - 显然操作系统做了一些非也是无比的忙碌等待.在2.6 GHz时,30%的CPU持续25秒,这是"无所事事"的相当多的周期.
知道如何解释这个吗?也许这里有人对Windows重叠IO的内部工作有更深入的了解?或者,您是否可以使用ReadFile读取兆字节的数据?
我可以看到IO调度程序如何通过最小化搜索来优化多个请求,尤其是当请求是随机访问时(它们不是!).我还可以看到,在NCQ中给出一些请求,硬盘如何能够执行类似的优化.
然而,我们谈论的是荒谬的一些荒谬的小要求 - 尽管如此,它们的表现仍然超过2倍的合理要求.
旁注:明显的赢家是内存映射.我几乎倾向于添加"毫不奇怪",因为我是内存映射的忠实粉丝,但在这种情况下,它实际上让我感到惊讶,因为"请求"甚至更小,操作系统应该更不能预测和安排IO.我最初没有测试内存映射,因为它似乎反直觉,甚至可以远程竞争.那么多你的直觉,嘿.
在不同偏移处重复映射/取消映射视图几乎为零时间.使用16MB视图并使用简单的for()循环对每个页面进行错误操作,每页读取一个字节,在9.2秒内完成@~111 MB/s.CPU使用率始终低于3%(一个核心).相同的计算机,相同的磁盘,相同的一切
虽然实际上只创建了一个页面,但Windows似乎一次只能将8个页面加载到缓冲区缓存中.每隔8页的错误以相同的速度运行并从磁盘加载相同数量的数据,但显示较低的"物理内存"和"系统缓存"指标以及仅1/8的页面错误.后续读取证明页面最终仍在缓冲区缓存中(没有延迟,没有磁盘活动).
(可能与内存映射文件非常非常相关,在巨大的顺序读取上更快?)
为了使它更具说明性:

更新:
使用FILE_FLAG_SEQUENTIAL_SCAN似乎有点"平衡"读取128k,将性能提高100%.另一方面,它严重影响512k和256k的读取(你不得不想知道为什么?)并且对其他任何东西没有实际影响.较小块大小的MB/s图表可能看起来更"均匀",但在运行时没有区别.

我可能已经找到了更小的块大小表现更好的解释.如您所知,如果操作系统可以立即(即从缓冲区)(以及各种版本特定的技术限制)提供请求,则异步请求可以同步运行.
在考虑实际异步与"立即"异步读取时,人们注意到超过256k,Windows异步运行每个异步请求.块大小越小,"立即"提供的请求就越多,即使它们不能立即可用(即ReadFile只是同步运行).我无法弄清楚一个明确的模式(例如"前100个请求"或"超过1000个请求"),但似乎请求大小和同步性之间存在负相关.在块大小为8k时,每个异步请求都是同步提供的.
由于某种原因,缓冲同步传输的速度是异步传输的两倍(不知道为什么),因此请求大小越小,整体传输速度越快,因为同步完成更多传输.
用于存储器映射prefaulting,FILE_FLAG_SEQUENTIAL_SCAN导致性能图(有一个"缺口",这是移动的位向后)的略微不同的形状,但所花费的总时间是完全相同(同样,这是令人惊讶的,但我不能帮助它).
更新2:
无缓冲IO使得为1M,4M的性能图,与512k的请求测试用例稍微更高和更"高低不平"与GB/s的90年代最大值,但是恶劣minumums也为1GB的总运行时间是在+/- 0.5缓冲运行的s(缓冲区大小较小的请求完成得更快,但这是因为超过2558个正在进行的请求,返回ERROR_WORKING_SET_QUOTA).在所有无缓冲的情况下,测量的CPU使用率为零,这并不令人惊讶,因为任何发生的IO都通过DMA运行.
另一个非常有趣的观察FILE_FLAG_NO_BUFFERING是它显着改变了API行为.CancelIO不再起作用,至少在取消IO的意义上是这样.对于无缓冲的飞行请求,CancelIO将直接阻止所有请求完成.律师可能会争辩说,这个职能部门不能因疏忽职责而承担责任,因为当它返回时没有剩下的飞行请求,所以在某种程度上它已经完成了所要求的 - 但我对"取消"的理解有点不同.
使用缓冲的,重叠的IO,CancelIO将简单地切断绳索,所有飞行中的操作立即终止,正如人们所期望的那样.
另一个有趣的事情是,在所有请求完成或失败之前,该过程是不可杀死的.如果操作系统在该地址空间中进行DMA操作,这种情况很有意义,但它仍然是一个令人惊叹的"功能".
我不是文件系统专家,但我认为这里有很多事情要做.首先.你对关于内存映射的评论是赢家.这并不奇怪,因为NT缓存管理器基于内存映射 - 通过自己进行内存映射,您可以复制缓存管理器的行为,而无需额外的内存副本.
当您从文件中按顺序读取时,缓存管理器会尝试为您预取数据 - 因此您可能会在缓存管理器中看到readahead的影响.在某些时候,缓存管理器停止预取读取(或者更确切地说,在某些时候,预取的数据不足以满足您的读取,因此缓存管理器必须停止).这可能是您所看到的较大I/O速度放缓的原因.
您是否尝试将FILE_FLAG_SEQUENTIAL_SCAN添加到CreateFile标志中?这指示预取者更具攻击性.
这可能是违反直觉的,但传统上从磁盘读取数据的最快方法是使用异步I/O和FILE_FLAG_NO_BUFFERING.当你这样做时,I/O直接从磁盘驱动器进入你的I/O缓冲区,没有任何东西妨碍(假设文件的段是连续的 - 如果它们不是,文件系统将不得不发出几个磁盘读取以满足应用程序读取请求).当然,这也意味着你丢失了内置的预取逻辑并且必须自己滚动.但是使用FILE_FLAG_NO_BUFFERING,您可以完全控制I/O管道.
还有一点需要记住:当您进行异步I/O时,确保始终有I/O请求非常重要 - 否则您将失去最后一次I/O完成与下一个I/O之间的潜在时间开始了.
| 归档时间: |
|
| 查看次数: |
1703 次 |
| 最近记录: |