【发布时间】:2013-08-20 22:54:12
【问题描述】:
这个问题是对我之前发布的一个问题的跟进:Windows fsync (FlushFileBuffers) performance with large files。我在哪里找到了可能的解决方案,但也找到了新问题。
在对 fsynced 写入的不同场景进行基准测试时,我发现了许多令人惊讶的结果。我希望有人可以帮助解释或指出解释这些结果的信息方向。
此基准测试的作用是将随机块(4096 字节大的页面)以 8 页 (32 K) 的批次顺序写入文件,然后刷新写入。它总共写入 200000 页,总计 800 MB 和 25000 次刷新。在开始写入之前,文件的大小设置为其最终长度。
一共支持4个选项,所有组合都运行:
- 在写入批处理或正常刷新 (NS) 后执行“fsync”/
FlushFileBuffers操作 (FS)。 - 在开始写入之前将单个字节写入文件的最后一个位置 (LB) 或将文件留空 (E)。
- 使用普通缓冲写入 (B) 或无缓冲/直写 (WT) 写入(使用 FILE_FLAG_NO_BUFFERING 和 FILE_FLAG_WRITE_THROUGH)。
- 直接写入文件流,即通过文件句柄 (F) 或使用内存映射 (MM) 间接写入文件。
下表总结了我在我的系统(带有慢速主轴磁盘的 64 位 Win 7 笔记本电脑)上对这些选项的所有组合的发现。
我发现,“fsynced”缓冲写入的性能随着文件大小呈指数下降到令人难以置信的低吞吐量,这使得在结合大文件时这样做不可行。如果文件的最后一个字节被写入(选项 LB),吞吐量会更低,所以我担心在随机而不是顺序写入场景中,性能会更加显着。
然而,令人惊讶的是,对于无缓冲/直写 I/O,吞吐量保持不变,与文件大小无关。最初(前 100-200 MB)它的吞吐量低于缓冲写入,但之后平均吞吐量迅速赶上并完成写入 800 MB 的速度大大加快。更令人惊讶的是,如果文件的最后一个字节被写入,吞吐量会增加 2 倍。
当通过内存映射文件写入文件时,性能会出现同样的指数下降,在使用无缓冲/直写标志打开文件的情况下也是如此。同样,如果文件的最后一个位置写入了一个字节,则性能会更差。
更新 根据霍华德的解释here 和here,我在开始写入之前重新运行了测试而不创建新文件(即打开现有的、完全写入的文件并覆盖它)。我已经更新了原始问题中的代码,以反映为此测试所做的更改。结果部分符合他对 Linux 的解释和发现。但也有一些值得注意的例外。下表提供了结果,红色突出显示显着变化,蓝色突出显示未发生变化的情况,这令人惊讶(即,如果霍华德解释中提到的影响是唯一起作用的影响,则与预期不符)。
对于使用“fsync”刷新的缓冲写入文件(即不通过 memmap),性能现在已从指数衰减变为恒定趋势。但是,现在比以前的测试场景需要更长的时间。吞吐量是恒定的 1.5 MB/s,之前它从 20 MB/s 左右开始呈指数衰减到 1.5 MB/s 左右。似乎一种可能的解释是文件元数据在每次刷新时也会被刷新,从而导致整个磁盘旋转以寻找元数据的位置。
对于“直写”到文件的场景,是否写入最后一个字节的结果现在是相同的,符合霍华德解释的预期。
然而,除了一个值得注意的例外,对内存映射的写入并没有真正改变,这令人惊讶。它们仍然显示出相同的写入性能指数衰减(从大约 20 MB/s 开始衰减到 1.8 MB/s)。这表明存在不同的机制。一个值得注意的例外是,如果底层文件是在没有 FILE_FLAG_WRITE_THROUGH 的情况下创建的,并且执行了“fsync”刷新。现在,此场景显示了恒定(较差)的性能,吞吐量约为 1.6 MB/s。由于我有一些疑问,我多次重新运行这个场景,每次都给出相同的结果。
为了了解更多信息,我还使用较小的文件(50000 页,总计 200 MB)重新运行了此测试,以确认 fsync 性能(对于缓冲 I/O)实际上确实取决于文件大小。结果如下所示,需要特别注意的以红色突出显示。
这些结果与较大文件的结果具有很好的相关性。值得注意的变化是,对于那些突出显示的内容,写入的性能更高一些,它们似乎达到了大约 7 MB/s 的限制。
根据目前对我的系统的观察总结为高度推测性结论:
- “fsync”在具有缓冲 IO 的文件(即没有 FILE_FLAG_WRITE_THROUGH 标志)上的 Windows 性能随着已写入文件的字节数呈指数下降。原因似乎是每次都需要刷新文件元数据,这会导致磁盘查找到文件的开头。
- 写入内存映射文件时,Windows 上的“fsync”性能也呈指数下降。我目前无法解释导致此问题的确切机制。
鉴于这种观察到的性能,至少对于我的用例而言,这两个 I/O 选项并不代表可行的解决方案。
根据Greg's suggestion,我将在关闭windows磁盘缓存的情况下重新运行测试,我还将运行霍华德提供的benchmark code,以排除由于我自己的错误导致结果出现偏差的可能性。
更新 2 我已经完成了测试,目前正在编译结果。为了不写“完整的历史”,我将用结果、调查结果和一些结论的摘要替换这个问题的当前内容。 Howard 对这个问题的回答,以及在 .NET 代码旁边运行他的 c 基准代码的能力是最有用的。这些结果与应用程序的相关性非常好。 Rlb 的回答帮助我更好地了解与磁盘相关的“合理数字”是什么。谢谢。
部分问题仍未得到解答。特别与写入内存映射时观察到的性能下降(和文件大小相关)有关。它可能与搜索/元数据刷新有关,但我还不清楚为什么/如何。
【问题讨论】:
-
如果您在最终时间报告收集之前关闭 tmpfile,这会改变数字吗?你的一些结果很有趣,所以也可以尝试重现。你运行场景的顺序有什么影响吗?
-
临时文件已关闭。该构造用于创建一个新的空文件,设置其长度并可选地填充最后一个字节。之后它被关闭。在开始写入之前重新打开文件。
-
@rlb 并且没有订单没有实际效果。由于我们每次都是从头开始创建文件并用随机字节填充它,因此不会发生缓存效果。独立运行场景会产生几乎相同的结果。
-
太好了,只是仔细检查。对于 exp-decrease 效果,您的硬盘驱动器是否可能包含一个小的内存写入缓存?这些对 fsync 和操作系统是不可见的,但是一旦它们溢出,写入性能就会下降。您可以使用获得确切的硬件/序列号。 Cmd> wmic 磁盘驱动器列表。然后 google/bing 应该会为您提供电气/技术详细信息
-
[我对 FlushFileBuffers 对内存映射文件的影响是错误的;评论已删除。]您能发布用于获取这些统计信息的代码吗?
标签: c# windows performance mmap fsync