【问题标题】:Why does VS2012 run identical tests at different speeds?为什么 VS2012 以不同的速度运行相同的测试?
【发布时间】:2012-11-30 14:37:35
【问题描述】:

我正在处理一个代码存在性能问题的项目。

我做了一些我认为会提高性能的更改,但没有真正的方法来衡量我的更改对它的影响。

我编写了一个单元测试,它按照当前实现的方式执行操作,并使用Stopwatch 来监控函数运行的速度。我还编写了一个类似的单元测试,它的处理方式略有不同。

如果测试一起运行,一个需要 1 秒才能完成,另一个需要 73 毫秒。 如果测试分开运行,它们都需要大约 1 秒才能完成(是的.. 我所做的更改似乎没有太大变化)。

如果测试相同,我有同样的问题,一个比另一个跑得快。

Visual Studio 是否在幕后做一些事情来提高性能?如果是,我可以关闭它吗?

我尝试将测试移动到不同的文件中,但没有解决我遇到的问题。

我希望能够运行所有测试,但让它们像一次只运行一个测试一样运行。

【问题讨论】:

  • 我认为这取决于代码。你需要初始化一些类,这在第二个测试中完成。
  • 我愿意打赌第一个测试总是比第二个测试运行得快,而且我也愿意打赌这是因为需要一些设置,而第一个测试就是这样做的,并且第二次测试不需要重做。
  • 如果您在调试器中运行,或者在调试构建的程序集中运行,或者使用分析器 API (Moles),则没有时间可以依赖。
  • 标准答案:使用分析器。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# unit-testing testing visual-studio-2012 performance-testing


【解决方案1】:

“过早的优化是万恶之源。”

如果您之前没有进行测量,您怎么知道您现在正在修复任何东西?你怎么知道你有需要解决的问题?

单元测试是为了操作正确性。它们可以用于性能,但我不会依赖它,因为许多其他因素会在运行时发挥作用。

最好的办法是获取分析器(或使用 VS 附带的分析器)并开始测量。

【讨论】:

  • 我们的应用程序已经发布了一段时间,在负载下现在出现了延迟。我已经运行了一个 SQL 分析器,发现它多次访问数据库,我可以通过将 nhibernate 提取模式更改为加入来阻止它。我认为我可以删除 NH 顶部的一个非常奇怪的抽象,这也将提高性能。这就是为什么我想检查新旧功能之间的时间。 - 问题是我需要能够对我的老板说这大致是我们将获得的改进,如果我有时间做这项工作(因为这是一个很大的重构)
  • 您的前两行没有解决 OP。它们也与已经提供的信息相矛盾。
  • @Trisped - 评论指向谁?
  • @StingyJack 它直接指向你(因为我没有@Jamez)。我试图礼貌地表明,如果没有前两行,你的答案会更好。
  • @Trisped - 但事实并非如此,因为性能测量是问题的核心。 OP 重构代码以使其“更好地”工作,但不可否认,在进行更改之前没有测量实际的先验条件(编写了一个类似的单元测试)。然后想要改进它并知道它性能更高,但使用相同的不正确(尽管有点类似)测量棒。我同意有一些明目张胆的事情可以提高性能,但如果 OP 处于这种试验水平,它可能并不明目张胆。
【解决方案2】:

我的猜测:很可能是 dll 加载和 JIT 编译

1。程序集加载。

.NET 延迟加载程序集(dll)。如果您添加对 FooLibrary 的引用,这并不意味着它会在您的代码加载时被加载。 相反,当您第一次从 FooLibrary 调用函数或实例化一个类时,然后CLR 将去加载它所在的 dll。这可能涉及在文件系统中搜索它安全检查等
如果您的代码甚至比较复杂,那么“第一次测试”通常最终会导致加载数十个程序集,这显然需要一些时间。
由于所有内容都已加载,后续测试看起来很快。

2。 JIT 编译

请记住,您的 .NET 程序集不包含 CPU 可以直接执行的代码。每当您调用任何 .NET 函数时,CLR 都会获取 MSIL 字节码并将其编译为可执行的机器代码,然后运行并运行该机器代码。它基于每个功能执行此操作。
因此,如果您考虑到第一次调用 any 函数时,在 JIT 编译时会有一点延迟,这些事情可以加起来。如果您要调用大量函数或初始化大型第三方库(想想实体框架等),这可能会特别糟糕。
如上所述,后续测试看起来很快,因为许多函数已经被 JIT 编译并缓存在内存中。

那么,你怎么能解决这个问题?

您可以通过减少程序集来缩短程序集加载时间。这意味着更少的文件搜索等等。 The microsoft .NET performance guidelines 详细说明。 另外,我相信将它们安装在全局程序集缓存中可能(??)会有所帮助,但我根本没有测试过,所以请多加注意。 安装到 GAC 需要管理权限,并且是一项重量级的操作。您不想在开发过程中这样做,因为它会给您带来问题(程序集从 GAC 加载而不是文件系统,因此您最终可以加载旧代码副本而无需意识到)。

您可以通过使用ngen 预编译您的程序集来改进 JIT 时间。但是,与 GAC 一样,这需要管理权限并且需要一些时间,因此您也不希望在开发过程中这样做。

我的建议?

首先,在单元测试中衡量性能并不是一件特别好的或可靠的事情。谁知道 Visual Studio 在后台执行的其他操作可能会或可能不会影响您的测试。

一旦你得到你的代码,你就试图在一个独立的应用程序中进行基准测试,让它循环并运行所有测试两次,然后丢弃第一个结果:-)

【讨论】:

  • 非常感谢您的详细解释!我不知道程序集加载和 JIT 编译,也不知道这两者是如何工作的! - 这真的很有意义!非常感谢!
猜你喜欢
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-01
相关资源
最近更新 更多