我的猜测:很可能是 dll 加载和 JIT 编译
1. 装配装载。
.NET 延迟加载程序集(dll)。如果您添加对 FooLibrary 的引用,这并不意味着它会在您的代码加载时被加载。相反,当你第一次从 FooLibrary 调用一个函数或实例化一个类时,CLR将去加载它所在的 dll。这涉及在文件系统中搜索它,可能的安全检查等。
如果你代码甚至中等复杂,那么“第一次测试”通常最终会导致加载数十个程序集,这显然需要一些时间。
随后的测试看起来很快,因为一切都已经加载。
2.JIT编译
请记住,您的 .NET 程序集不包含 CPU 可以直接执行的代码。每当您调用任何 .NET 函数时,CLR 都会获取 MSIL 字节码并将其编译为可执行的机器代码,然后运行并运行该机器代码。它在每个功能的基础上执行此操作。
因此,如果您考虑到第一次调用任何函数时,它在 JIT 编译时会有一点延迟,这些东西可以加起来。如果您要调用大量函数或初始化大型第三方库(想想实体框架等),这可能会特别糟糕。
如上所述,后续测试看起来很快,因为许多函数已经被 JIT 编译并缓存在内存中。
那么,你怎么能绕过这个呢?
您可以通过减少程序集来缩短程序集加载时间。这意味着更少的文件搜索等等。microsoft .NET 性能指南更详细。另外,我相信将它们安装在全局程序集缓存中可能(??)会有所帮助,但我根本没有测试过,所以请多加注意。安装到 GAC 需要管理权限,并且是一项重量级的操作。您不想在开发期间这样做,因为它会导致您出现问题(程序集从 GAC 加载而不是文件系统,因此您最终可能会在没有意识到的情况下加载代码的旧副本)。
您可以通过使用ngen预编译您的程序集来改进 JIT 时间。但是,与 GAC 一样,这需要管理权限并且需要一些时间,因此您也不希望在开发期间这样做。
我的建议?
首先,在单元测试中测量性能并不是一件特别好的或可靠的事情。谁知道 Visual Studio 在后台执行的其他操作可能会或可能不会影响您的测试。
一旦你得到你的代码,你就试图在一个独立的应用程序中进行基准测试,让它循环并运行所有测试两次,然后丢弃第一个结果:-)