.NET 4.0运行时比.NET 2.0运行时慢吗?

23

我将项目升级到.NET 4.0(使用VS2010),发现其运行速度比.NET 2.0(VS2008)慢。因此,我决定在VS2008和VS2010中使用各种目标框架对简单控制台应用程序进行基准测试:

using System;
using System.Diagnostics;
using System.Reflection;

namespace RuntimePerfTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Assembly.GetCallingAssembly().ImageRuntimeVersion);
            Stopwatch sw = new Stopwatch();

            while (true)
            {
                sw.Reset();
                sw.Start();

                for (int i = 0; i < 1000000000; i++)
                {

                }

                TimeSpan elapsed = sw.Elapsed;
                Console.WriteLine(elapsed);
            }
        }
    }
}

以下是结果:

  • VS2008
    • 目标框架 2.0: ~0.25 秒
    • 目标框架 3.0: ~0.25 秒
    • 目标框架 3.5: ~0.25 秒
  • VS2010
    • 目标框架 2.0: ~3.8 秒
    • 目标框架 3.0: ~3.8 秒
    • 目标框架 3.5: ~1.51 秒
    • 目标框架 3.5 客户端配置文件: ~3.8 秒
    • 目标框架 4.0: ~1.01 秒
    • 目标框架 4.0 客户端配置文件: ~1.01 秒

我的初步结论显然是,用VS2008编译的程序比用VS2010编译的程序运行更快。

有人能解释一下在VS2008和VS2010之间以及在VS2010内部不同的目标框架之间性能变化的原因吗?


6
你是在 Visual Studio 的调试器中运行这些应用程序还是以发布模式运行这些应用程序? - Mark Redman
2
你测试了多次以减少外部影响吗?目前的结果很可能只是由于偶然因素造成的。虽然看起来具有决定性,但在这个测试中,单次运行几乎没有任何显著意义。 - Konrad Rudolph
你尝试过在 Visual Studio 之外运行它吗? - eflles
3
你的示例程序似乎有缺陷。循环体为空,因此基本上被优化器移除了。 - Dirk Vollmar
我在“Release”模式下运行所有测试,并且没有附加调试器。两个项目都是干净的,所以我认为没有设置差异,或者VS2010的默认设置会减慢程序的速度?:| - DxCK
@DxCK:看看我的修改后的答案。我打赌你正在运行64位机器,但VS2010是构建目标x86... - Jon Skeet
4个回答

28
我想我明白了。
如果你在64位机器上运行,请确保构建设置为“Any CPU”而不是“x86”。这样做可以解决我的机器上的问题。
在VS2010中,新项目的默认值从“Any CPU”更改为“x86” - 我认为这是为了使编辑和继续在64位机器上默认工作(因为它仅支持x86)。
在64位机器上运行x86进程显然有些次优。
编辑:根据达斯汀的评论,与x64相比,运行x86可以在更有效地使用内存(更短的引用)方面具有性能优势。
我还通过电子邮件与达斯汀进行了通信,他提到了以下原因:
FWIW,默认目标平台并没有改变以支持ENC。我们已经发布了2个版本的x64上损坏的ENC。因此,ENC本身并不是一个强制切换的理由。我们切换的主要原因(无特定顺序)是:
IntelliTrace不支持x64。因此,“Any CPU”项目在x64 Windows上将无法使用最酷的新功能之一。
x64 EXE 在x64 Windows上运行速度比x86 EXE 慢。因此,“优化”的构建在Release中实际上会表现更差。
客户投诉部署应用程序时发现它无法正常工作,即使它在他们的机器上也能正常工作。这些问题通常围绕P/Invoke,但在应用程序运行时可以做出许多其他假设,这些假设可能会在不同位数下运行时破坏。
以上原因加上一个任何CPU带来的好处都没有(即你实际上不能利用扩展的地址空间,因为EXE仍然可能在x86处理器上运行)的事实。

将默认设置更改为x64是因为越来越多的计算机配备了64位处理器(相对于32位的x86)。

Rick Byers在这个主题上有一篇很棒的文章


1
可能差异在于平台目标吗?csc.exe使用AnyCPU作为默认值,而VS 2010已将默认值更改为x86。 - Dirk Vollmar
4
是的,但在我写下评论时,你的回答还没有提到这一点;-) 我希望如果有人更新他们的答案,能够立即刷新页面。 - Dirk Vollmar
1
更改为 Any CPU 后,在所有目标框架中我得到了约 0.25 秒的时间,就像在 VS2008 中一样,这很酷!但这仍然无法解释在 x86 平台上使用不同目标框架时性能差异的原因。 - DxCK
事实上,更改默认平台目标的动机之一是为了进行编辑和继续操作,以及P/Invoke和COM互操作方案。但是,此更改不影响类库。在这里,默认值仍为AnyCPU。有关更详细的说明,请参见https://connect.microsoft.com/VisualStudio/feedback/details/455333/platform-target-is-defaulting-to-x86-rather-than-any-cpu。 - Dirk Vollmar
1
就我所知,在64位机器上运行x86进程并不是次优的选择。一般来说,在64位机器上运行x86进程实际上比运行x64进程更快。请看下面我的回复,了解我为什么认为基准测试有缺陷。 - Dustin Campbell
显示剩余2条评论

9

我认为你的基准测试有缺陷。在发布模式下,你样例程序在VS 2008和VS 2010中的IL代码是相同的(VS 2008针对.NET 2.0,VS 2010使用默认设置针对.NET 4.0)。因此,你不应该看到VS 2008和VS 2010之间时间上的差异。两个编译器都会生成以下代码:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       69 (0x45)
  .maxstack  2
  .locals init ([0] class [System]System.Diagnostics.Stopwatch sw,
           [1] int32 i,
           [2] valuetype [mscorlib]System.TimeSpan elapsed)
  IL_0000:  call       class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetCallingAssembly()
  IL_0005:  callvirt   instance string [mscorlib]System.Reflection.Assembly::get_ImageRuntimeVersion()
  IL_000a:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000f:  newobj     instance void [System]System.Diagnostics.Stopwatch::.ctor()
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  callvirt   instance void [System]System.Diagnostics.Stopwatch::Reset()
  IL_001b:  ldloc.0
  IL_001c:  callvirt   instance void [System]System.Diagnostics.Stopwatch::Start()
  IL_0021:  ldc.i4.0
  IL_0022:  stloc.1
  IL_0023:  br.s       IL_0029
  IL_0025:  ldloc.1
  IL_0026:  ldc.i4.1
  IL_0027:  add
  IL_0028:  stloc.1
  IL_0029:  ldloc.1
  IL_002a:  ldc.i4     0x3b9aca00
  IL_002f:  blt.s      IL_0025
  IL_0031:  ldloc.0
  IL_0032:  callvirt   instance valuetype [mscorlib]System.TimeSpan [System]System.Diagnostics.Stopwatch::get_Elapsed()
  IL_0037:  stloc.2
  IL_0038:  ldloc.2
  IL_0039:  box        [mscorlib]System.TimeSpan
  IL_003e:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0043:  br.s       IL_0015
} // end of method Program::Main

可能有一件事情不同的是平台目标。VS 2010使用x86作为默认的平台目标,而VS 2008使用AnyCPU。如果您在64位系统上,这将导致对VS 2008与VS 2010构建使用不同的JIT编译器。由于JIT编译器是分别开发的,因此可能会导致不同的结果。


5

我认为这个基准测试存在缺陷。

  • 它太简短了。
  • 正如之前指出的,x86/x64的不同JIT很可能会以不同的方式优化循环。
  • 它实际上只测试了可能被JIT编译成快速寄存器访问的堆栈变量。一个更真实的基准测试应该至少移动地址空间。

在x86的情况下,大部分额外时间可能是由WoW层占用的。然而,在一个更长的基准测试中,x64进程固有的低效性很可能会超过WoW层的开销,尤其是当基准测试实际上触及内存时(通过在堆上创建和访问对象),你将看到WoW层指针优化的好处。


0
我们有同样的问题。将WPF项目从.NET 3.5(VS2008)转换为.NET 4(VS2010)后,GUI响应速度明显降低(每次点击几乎需要1秒钟的延迟)。
经过一些调查,我们发现这是因为Visual Studio 2010消耗更多的资源,当我们从VS2010进行调试时,所有操作都变得更慢了。但是,当我们运行已构建的项目作为.exe文件时,它又可以快速运行了。

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