应用程序冷启动时,FileStream非常缓慢。

10
如果您感兴趣,SO这里也问了一个类似的问题,但是正如我们将看到的,那个问题的被接受答案并不总是适用(对我的应用程序使用模式从未适用)。
性能决定代码由FileStream构造函数(打开文件)和SHA1哈希(.Net框架实现)组成。该代码几乎是我上面链接到的问题所要求的C#版本。
情况1:应用程序第一次启动或第N次启动,但设置了不同的目标文件。现在要求应用程序计算以前从未访问过的文件的哈希值。 - ~50ms - 80%的FileStream构造函数 - 18%的哈希计算
情况2:现在完全终止应用程序,并再次启动,要求在相同的文件上计算哈希值: - ~8ms - 90%的哈希计算 - 8%的FileStream构造函数
问题: 我的应用程序始终处于使用Case 1状态,永远不会被要求重新计算已经访问过一次的文件的哈希值。 因此,我的速率限制步骤是FileStream Constructor!我能做些什么来加快这种使用情况吗?
谢谢。
注:使用JetBrains分析器收集了统计信息。

1
我看到了完全相同的行为。使用ReadAllBytes并计算哈希需要很短的时间,但可能会对内存造成严重影响(取决于文件大小)。因此,我尝试将FileStream传递给MD5的computehash($stream),但结果完全不可接受。结果时间相差几个数量级... - thepip3r
1
从查看FileStream源代码来看,似乎有大量的预处理指令,即使在最简单的路径中,初始化也似乎相当复杂。您可能需要指定您遇到问题的特定构造函数以及您的环境,并希望比我聪明得多的人可以帮助您。 - Griswald_911
你是否已经正确地实现了多线程?是的,虽然主轴数量有限,但当需要逐个访问大量小文件时,您可能会花费大部分时间等待数据。通过使用多线程,您可以稍微(或在SSD中更多地)加快速度。虽然不是“极端”的情况,但使用2-3个线程可能有助于减少停机时间。 - TomTom
需要更多信息。您如何调用构造函数?您的参数是什么,它们看起来像什么?绝对路径还是相对路径?您是否使用ngen来减少/删除JIT开销?您是否在受限信任场景中运行,还是您的应用程序具有完全信任?您是否尝试使用FileOptions.SequentialScan打开文件?您是否尝试通过ProfileOptimization.StartProfile启用多核jit? - antiduh
@antiduh,任何需要10秒钟的操作显然不是这些选项的功能,而是一个我不指望得到修复的错误。 - D Left Adjoint to U
@DanielDonnelly - 你回答了一个并未被问到的问题。你不是发帖人,而且发帖人没有指明他们遇到了10秒延迟的问题;他们的延迟是50毫秒。请不要试图改变另一个发帖人问题的主题;相反,提出自己的问题。如果由于声望低而难以提出自己的问题,我建议你阅读帮助中心关于如何提出有生产力的问题的内容。 - antiduh
5个回答

3

...但是目标文件集不同。

关键短语,您的应用程序将无法充分利用文件系统缓存。就像第二次测量一样。目录信息不能来自RAM,因为它还没有被读取,操作系统总是必须退回到磁盘驱动器,这很慢。

只有更好的硬件才能加速它。50毫秒是一个主轴驱动器所需的标准时间,20毫秒大约是这种驱动器可以达到的最小值。读头寻道时间是硬机械限制。今天很容易超越,SSD已经广泛可用且价格合理。唯一的问题是当你习惯了它之后,就再也不想回去了 :)


1

文件系统和/或磁盘控制器将缓存最近访问的文件/扇区。

决定速率的步骤是读取文件,而不是构造FileStream对象,当数据在缓存中时,第二次运行会显着更快,这是完全正常的。


我不相信这是真的。FileStream构造函数并不会读取整个文件,哈希函数才是为此而调用的。但是构造函数却占据了80%的时间。 - Alex K

1
如前所述,文件系统有自己的缓存机制,这会干扰您的测量。
然而,FileStream构造函数执行多项任务,第一次需要访问文件系统(因此可能不在数据缓存中),因此是昂贵的。出于说明原因,您可以查看代码,并查看CompatibilitySwitches类用于检测子功能使用情况。Reflection与此类一起直接大量使用(用于访问当前程序集)和间接使用(用于CAS保护部分,安全链接要求)。Reflection引擎有自己的缓存,并在其自己的缓存为空时需要访问文件系统。
两个测量结果如此不同感觉有点奇怪。我们目前在配备实时保护的防病毒软件的计算机上有类似的情况。在这种情况下,防病毒软件位于中间,缓存命中或未命中取决于此类软件的实现。
防病毒软件可能会决定积极检查某些图像文件,例如PNG,由于已知的解码漏洞。这些检查会引入额外的减速,并将时间计入最外层的.NET类,即FileStream类。
使用本地符号和/或内核调试进行分析应该可以给您更多见解。
根据我的经验,您所描述的问题无法得到缓解,因为存在多个我们无法控制的隐藏层。根据您目前并不完全清楚的使用情况,您可以将应用程序转换为服务,从而更快地处理所有后续请求。或者,您可以将多个请求批量处理为一个单一调用,以实现摊销降低成本。

1

离题建议,但这是我经常做的事情,可以使我们的分析速度提高30%-70%:

缓存


编写另一段代码,以执行以下操作:

  • 迭代所有文件;
  • 计算哈希值;并且
  • 将其存储在另一个索引文件中。

现在,在应用程序启动时不要调用FileStream构造函数来计算哈希。相反地,打开(预期会小得多的)索引文件并从中读取预先计算的哈希值。

此外,如果这些文件是日志等文件,在应用程序启动之前每次都会被新创建,请在文件创建者中添加代码,以便使用新创建文件的哈希值更新索引文件。

这样,您的应用程序就可以仅从索引文件中读取哈希值。


我同意 @HansPassant 的建议,使用固态硬盘可以加快磁盘读取速度。这个答案和他的答案互补。你可以同时实现两者以最大化性能。


我的问题是加载一个简单的8KB png需要15秒钟。 - D Left Adjoint to U
@DanielDonnelly:请编辑问题并将您的完整代码添加到其中...作为情况的示例。这是非常奇怪的行为。这可能是由于您使用的任何本地库引起的吗? - displayName
他们已经阻止我提问了。每次他们解除封禁后,我都会提出完美的问题,然后再次被封禁。请查看我的历史记录。因此,我无法提问。即使我可以完全创建一个最小工作示例并用英语完美地解释它。 - D Left Adjoint to U
@DanielDonnelly:虽然您可能无法提出新问题,但您可以编辑此问题并添加您的代码。这是可能的吗? - displayName
@displayName - 这不是一个好主意。丹尼尔不应该修改别人的问题来为自己服务。如果他由于声望低而无法提出问题,在与SO的反复互动之后,这表明他不是以诚信参与。 - antiduh
显示剩余3条评论

-1

2
如果您传递FileOptions.SequentialScan,那么使用接受FileOptions构造函数是否已经对流执行了此操作?编辑:是的,在查看参考源代码时,该枚举参数被转换为CreateFiledwFlagsAndAttributes,而枚举的值为0x08000000,这与FILE_FLAG_SEQUENTIAL_SCAN的值相同。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接