【问题标题】:Running a managed application 2nd time shows different performance than 1st第二次运行托管应用程序显示的性能与第一次不同
【发布时间】:2010-10-03 02:10:12
【问题描述】:

我有一个基准测试应用程序来测试我编写的一些 API 的性能。在这个基准测试应用程序中,我基本上使用 QueryPerformanceCounter 并通过将调用 API 前后 QPC 值的差异除以频率来获取时间。但是如果我从不同的驱动器运行应用程序(在同一组 Dll 上运行的相同可执行文件),基准测试结果似乎会有所不同。此外,在特定驱动器上,第一次运行应用程序、关闭应用程序并再次运行它会产生不同的基准测试结果。谁能解释这种行为?我在这里遗漏了什么吗?

一些更有用的信息:

行为是这样的:运行应用程序,关闭它并再次运行它,基准测试结果似乎在第二次运行时有所改善,此后保持不变。这种行为在从 C 盘运行的情况下更加突出。 我还想提一下,我的基准应用程序可以选择重新运行/重新测试特定的 API,而无需关闭应用程序。我确实知道这涉及到 jitting,但我不明白的是,在第一次运行应用程序时,当您在不关闭应用程序的情况下多次重新运行 API 时,性能在运行几次后稳定下来,然后当您关闭并重新运行时同样的测试,性能似乎有所提升。

另外,您如何考虑从不同驱动器运行时的性能变化?

[信息更新]

我做了一个 ngen,现在从同一位置进行的不同运行之间的性能差异消失了。即,如果我打开基准应用程序,运行一次,关闭它并从同一位置重新运行它,它会显示相同的值。

但是我现在遇到了另一个问题。当我从 D 驱动器启动应用程序并运行它几次(在同一启动基准程序中的 API 迭代几次),然后从第 3 次迭代开始,所有 API 的性能似乎下降了 20% 左右.然后,如果您关闭并重新启动应用程序并运行它,对于前 2 次迭代,它会给出正确的值(与从 C 获得的值相同),然后性能再次超出该值。从 C 盘运行时看不到此行为。从C盘,无论你跑多少次,都是相当一致的。

我正在使用大型双精度数组来测试我的 API 性能。我担心 GC 会在测试之间启动,所以我在每次测试之前和之后都明确地调用 GC.Collect() 和 GC.WaitForPendingFinalizers()。所以我认为这与GC无关。

我尝试使用 AQ 时间来了解从第 3 次迭代开始发生的情况,但有趣的是,当我使用 AQ 时间分析运行应用程序时,性能根本没有下降。

性能计数器 as 没有显示任何有趣的 IO 活动。

谢谢 尼兰詹

【问题讨论】:

  • Cory 对一个可能的问题做出了正确的回答。但是,如果您可以详细说明结果,那将有所帮助。例如,如果第二次运行更糟糕,那就是另一回事了。 :)
  • 抱歉之前没有进行描述。行为是这样的:运行应用程序,关闭它并再次运行它,基准测试结果似乎在第二次运行时有所改善,此后保持不变。这种行为在从 C 盘运行的情况下更加突出。

标签: performance api benchmarking performancecounter


【解决方案1】:

我认为这里有多种效果:

首先,在测试工具中多次运行相同的功能,每次使用相同的数据,可能会有所改善,因为:

  • JIT 编译将优化运行最频繁的代码以提高性能(如 Cory Foy 的 mentioned already)
  • 程序代码将在磁盘缓存中(如 Crashwork 的 mentioned already)
  • 如果程序代码足够小并且执行足够频繁,某些程序代码将在 CPU 缓存中

如果测试工具中函数每次运行的数据不同,这可以解释为什么再次关闭和运行测试工具会改善结果:数据现在也 em> 在磁盘缓存中,这已经不是第一次了。

最后,是的,即使两个“驱动器”位于同一个物理磁盘上,它们也会有不同的性能:从磁盘外部读取数据的速度比内部读取数据的速度更快。如果它们是不同的物理磁盘,那么性能差异似乎很可能。此外,一个磁盘可能比另一个磁盘碎片更多,从而导致更长的寻道时间和更慢的数据传输速率。

【讨论】:

    【解决方案2】:

    此外,其他因素可能也在起作用。机器上的文件系统缓存,最近使用的数据的缓冲等。

    除非您专门测量冷启动时间,否则最好运行多个测试(或数百个!)并在整个集合中取平均值。

    【讨论】:

      【解决方案3】:

      运行应用程序会将其可执行文件和其他文件从硬盘驱动器放入操作系统的磁盘缓存(在 RAM 中)。如果稍后再次运行,这些文件中的许多可能仍在缓存中。 RAM 比磁盘快得多。

      当然,一个磁盘可能比另一个更快。

      【讨论】:

        【解决方案4】:

        是的。它被称为Just-In-Time compiling。基本上,您的应用程序被部署为 MSIL(Microsoft 中间语言),并且在第一次运行时它会被转换为本机代码。

        您始终可以运行 NGen(请参阅上面的文章),或者在性能测试脚本中设置一个预热期,在实际对性能进行基准测试之前,它会在场景中运行几次。

        【讨论】:

        • 真的吗?它实际上是否在运行之间存储了 JIT 的代码?从那个网页上看,似乎并非如此。
        • 我认为更可能是 DLL 已加载到内存中。
        • 我想在这里提一下,我的基准应用程序可以选择重新运行/重新测试特定的 API,而无需关闭应用程序。
        • 我确实了解 jitting 部分,但我不明白的是,在第一次运行应用程序时,当您在不关闭应用程序的情况下多次重新运行 API 时,运行几次后性能稳定,然后当您关闭并重新运行相同的测试时,性能似乎有所提高。
        • 另外,您如何考虑从不同驱动器运行时的性能变化?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多