【问题标题】:FileStream very slow on application-cold startFileStream 在应用程序冷启动时非常慢
【发布时间】:2010-12-12 10:06:25
【问题描述】:

如果您有兴趣,还向here on SO 提出了一个非常相似的问题,但我们将看到该问题的公认答案并非总是如此(而且我的应用程序使用模式也并非如此)。

性能确定代码由 FileStream 构造函数(用于打开文件)和 SHA1 哈希(.Net 框架实现)组成。该代码几乎是我在上面链接的问题中提出的问题的 C# 版本。

案例 1: 应用程序第一次或第 N 次启动,但设置了不同的目标文件。现在告诉应用程序计算以前从未访问过的文件的哈希值。

  • ~50ms
  • 80% FileStream 构造函数
  • 18% 哈希计算

案例 2: 应用程序现在完全终止,然后重新启动,要求计算相同文件的哈希:

  • ~8ms
  • 90% 哈希计算
  • 8% FileStream 构造函数

问题
我的应用程序一直在使用案例 1。永远不会要求它重新计算已经访问过一次的文件的哈希值。

所以我决定速率的步骤是 FileStream 构造函数!我能做些什么来加快这个用例的速度吗?

谢谢。

附:使用 JetBrains 分析器收集统计数据。

【问题讨论】:

  • 我看到了完全相同的行为。使用 ReadAllBytes 并计算哈希需要很短的时间,但在内存上可能很糟糕(取决于文件的大小)。所以我尝试将 FileStream 传递给 MD5 的 computehash($stream) 并看到完全不可接受的结果。结果时间相差几个数量级......
  • @Alex K:从FileStream source 看来,似乎有大量的预处理器指令,即使在最简单的路径中,初始化似乎也相当复杂。您可能想要指定您遇到问题的特定构造函数以及您的环境是什么样的,希望比我更聪明的人(很多)可以帮助您。
  • 你是正确的多线程吗?是的,主轴是有限的,但是当一个一个访问大量小文件时,您可能会花费大部分时间等待数据,并且您可以通过多线程来加快速度(或在 SSD 中加快速度)。不是“极端”,但 2-3 个线程可能有助于减少停机时间。
  • 需要更多信息。你究竟是如何调用构造函数的?你的论点是什么,它们是什么样的?绝对路径还是相对路径?您是否正在编写程序以减少/消除 JIT 开销?您是在有限信任场景中运行,还是您的应用程序具有完全信任?您是否尝试过使用FileOptions.SequentialScan 打开文件?您是否尝试过通过ProfileOptimization.StartProfile 启用多核 jit?
  • @antiduh 任何需要 10 秒的东西显然不是这些选项的功能,而是一个错误,我不希望得到修复。

标签: c# .net windows performance hard-drive


【解决方案1】:

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

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

只有更好的硬件才能加快速度。 50 毫秒大约是主轴驱动器所需的标准时间量,20 毫秒大约是此类驱动器所能达到的最低时间。读卡器磁头寻道时间是硬机械限制。这在今天很容易被击败,SSD 广泛可用且价格合理。唯一的问题是,当你习惯了它,你就再也不会退缩了:)

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我不相信是这种情况。 FileStream 构造函数不会读取整个文件,为此目的会调用哈希函数。但是构造函数占用了 80% 的时间。
    【解决方案3】:

    偏离轨道的建议,但这是我做了很多的事情,我们的分析速度提高了 30% - 70%:

    缓存


    编写另一段代码:

    • 遍历所有文件;
    • 计算哈希值;并且,
    • 将其存储在另一个 index 文件中。

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

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

    这样您的应用程序始终只能从索引文件中读取哈希值。


    我同意@HansPassant 的建议,即使用 SSD 让您的磁盘读取速度更快。这个答案和他的答案是互补的。您可以同时实施这两种方法以最大限度地提高性能。

    【讨论】:

    • 我的问题是加载一个简单的 8KB png 需要 15 秒。
    • @DanielDonnelly:8KB 大小的数据根本不足以加载 15 秒。可能是多个程序正在访问您的硬盘驱动器。为此,请尝试将您的数据放在另一个驱动器上,看看访问时间是否几乎是瞬时的。另外,您的驱动器在网络上吗?
    • 不,不在网络上。奇怪的是,我机器上的所有其他程序都可以正常工作,除了一个尝试加载小纹理并且我使用 Monogame 用 C# 编写的程序。这导致我停止该项目并且如果我再次开始它就不再使用 C#。我需要即时生成的图像是少量文本的 LaTeX 渲染。甚至 LaTeX 的渲染速度也更快:2 秒,这对于我在缓存下的应用程序来说是可以接受的,但是 10-15 秒的等待是不可用的。我刚开始使用 monogame,所以我的环境是 100% 的最新版本。
    • @DanielDonnelly:请编辑问题并在其中添加您的完整代码......作为情况的示例。这是非常奇怪的行为。可能是由于您正在使用任何本机库吗?
    • 他们阻止我提问。每次他们解禁我时,我都会问一个完美的问题,然后再次被禁止。看我的历史。所以我不能问一个问题。尽管我 100% 能够创建一个最小的工作示例并用英语完美地解释它。
    【解决方案4】:

    如前所述,文件系统有自己的缓存机制,它会扰乱您的测量。

    然而,FileStream 构造函数执行了几个任务,第一次是昂贵的并且需要访问文件系统(因此可能不在数据缓存中的某些内容)。出于解释原因,您可以查看代码,并看到CompatibilitySwitches 类用于检测子功能使用情况。与这个类一起,反射在直接(访问当前程序集)和间接(对于 CAS 保护部分、安全链接需求)中都被大量使用。反射引擎有自己的缓存,当自己的缓存为空时需要访问文件系统。

    这两个测量值如此不同,感觉有点奇怪。目前,我们的机器上也有类似的东西,配备了配置了实时保护的防病毒软件。在这种情况下,杀毒软件在中间,缓存是第一次命中还是未命中取决于该软件的实现。

    由于已知的解码漏洞,防病毒软件可能决定积极检查某些图像文件,例如 PNG。此类检查会导致额外的减速并在最外层的 .NET 类(即 FileStream 类)中计算时间。

    使用本机符号和/或内核调试进行分析,应该会给您更多的见解。

    根据我的经验,您所描述的情况无法减轻,因为我们无法控制多个隐藏层。根据您的使用情况,我现在还不是很清楚,您可以将应用程序转换为服务,因此您可以更快地处理所有后续请求。或者,您可以将多个请求批处理到一个调用中,以实现摊销降低成本。

    【讨论】:

      【解决方案5】:

      你应该尝试使用原生的FILE_FLAG_SEQUENTIAL_SCAN,你必须 pinvoke CreateFile 以获得句柄并将其传递给FileStream

      【讨论】:

      • 不使用constructor 接收FileOptions 是否已经将它传递给流FileOptions.SequentialScan?编辑:是的,确实如此,通过参考源查看枚举参数变成CreateFiledwFlagsAndAttributes,枚举的值为0x08000000,与FILE_FLAG_SEQUENTIAL_SCAN 的值相同
      猜你喜欢
      • 1970-01-01
      • 2015-07-19
      • 1970-01-01
      • 2016-07-18
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      • 1970-01-01
      相关资源
      最近更新 更多