.NET 4.0 Runtime比.NET 2.0 Runtime慢吗?

DxC*_*xCK 22 .net performance benchmarking .net-4.0 visual-studio-2010

在我将项目升级到.NET 4.0(使用VS2010)后,我意识到它们运行速度比在.NET 2.0(VS2008)中慢.所以我决定使用各种Target Frameworks对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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

  • 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秒
    • Target Framework 3.5客户端配置文件:~3.8秒
    • 目标框架4.0:~1.01秒
    • Target Framework 4.0客户端配置文件:~1.01秒

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

任何人都可以解释VS2008和VS2010之间的性能变化吗?在VS2010内部的不同目标框架之间?

Jon*_*eet 27

我想我已经明白了.

如果您在64位计算机上运行,​​请确保将构建设置为"任何CPU"而不是"x86".这样做解决了我的机器上的问题.

VS2010中新项目的默认值从"任何CPU"更改为"x86" - 我相信这是为了使编辑和继续在64位计算机上默认工作(因为它只支持x86).

在64位计算机上运行x86进程显然有些不理想.

编辑:根据Dustin的评论,运行x86而不是x64可以在更有效地使用内存(更短的引用)方面具有性能优势.

我还通过电子邮件与Dustin通信了,他包括以下原因:

FWIW,默认目标平台未更改为支持ENC.我们已经在x64上发布了2个版本的ENC.因此,ENC本身并不是一个令人信服的理由.我们切换的主要原因(无特定顺序)是:

  • x64不支持IntelliTrace.因此,最酷的新功能之一将不适用于任何CPU项目的x64 Windows.

  • x64 EXE在x64 Windows上的运行速度比x86 EXE慢.因此,x86调试,x64版本的想法意味着Release中的"优化"版本实际上会表现更差.

  • 客户投诉部署应用程序并发现它不起作用,即使它在他们的计算机上运行.这些通常是围绕P/Invoke进行的,但是在应用程序中可以进行许多其他假设,这些假设在以不同的位数运行时可能会中断.

上述原因加上Any CPU没有带来任何好处(即你实际上无法利用扩展的地址空间,因为EXE可能仍然在x86上运行)是默认切换的原因.

Rick Byers在这里有一篇关于这个主题的优秀文章 .

  • 是的,确实如此,但是当我写评论时,你的回答并没有说出来;-)如果有人更新他们的答案,我希望能立即刷新. (4认同)

Dir*_*mar 8

我相信你的基准是有缺陷的.来自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
Run Code Online (Sandbox Code Playgroud)

可能有所不同的是平台目标.VS 2010使用x86VS 2008作为默认平台目标AnyCPU.如果您使用的是64位系统,则会导致将不同的JIT编译器用于VS 2008与VS 2010版本.这可能导致不同的结果,因为JIT编译器是单独开发的.


Dus*_*ell 5

我同意基准是有缺陷的.

  • 它太短了.
  • 如前所述,x86/x64的不同JIT可能会以不同方式优化循环.
  • 它实际上只测试可能JITted快速注册访问的堆栈变量.一个更真实的世界基准应该至少移动访问地址空间.

大多数额外时间可能是x86案例中的WoW层.但是,x64进程固有的低效率很可能会超过实际接触内存的较长基准测试中WoW层的开销.事实上,如果基准测试是访问内存(通过创建和访问堆上的对象),您将看到WoW层指针优化的好处.