C# 返回值或通过操作返回之间奇怪的性能差异<;T>;参数

C# 返回值或通过操作返回之间奇怪的性能差异<;T>;参数,c#,.net,performance,delegates,C#,.net,Performance,Delegates,我很想知道从方法返回值与通过动作参数返回值之间的性能差异 有一个与此相关的问题 但就我的一生而言,我无法解释为什么返回值要比调用委托返回值慢约30%。是.net抖动(不是编译器..)在我的简单委托中(我认为它没有这样做) 类程序 { 静态void Main(字符串[]参数) { 秒表sw=新秒表(); sw.Start(); A aa=新的A(); 长l=0; 对于(int i=0;il+=r); } sw.Stop(); Trace.WriteLine(sw.elapsedmillyses+

我很想知道从方法返回值与通过动作参数返回值之间的性能差异

有一个与此相关的问题

但就我的一生而言,我无法解释为什么返回值要比调用委托返回值慢约30%。是.net抖动(不是编译器..)在我的简单委托中(我认为它没有这样做)

类程序
{
静态void Main(字符串[]参数)
{
秒表sw=新秒表();
sw.Start();
A aa=新的A();
长l=0;
对于(int i=0;i<100000000;i++)
{
剂量测定法(i-1,i,r=>l+=r);
}
sw.Stop();
Trace.WriteLine(sw.elapsedmillyses+“:”+l);
sw.Reset();
sw.Start();
l=0;
对于(int i=0;i<100000000;i++)
{
l+=aa.剂量物质2(i-1,i);
}
sw.Stop();
Trace.WriteLine(sw.elapsedmillyses+“:”+l);
}
}
甲级
{
私有B bb=新B();
公共无效剂量测定(int a、int b、行动结果)
{
bb.添加(a、b、结果);
}
公共长剂量物质2(int a、int b)
{
返回bb.Add2(a,b);
}
}
B类
{
公共无效添加(int a、int b、操作结果)
{
结果(a+b);
}
公共长地址2(int i,int i1)
{
返回i+i1;
}
}

为什么不使用查找?

奇怪的是,我没有看到您在中运行发布版本时所描述的行为,而不是在运行调试版本时所描述的行为。我唯一能想到的是,在运行调试构建时,基于返回的方法会增加额外的开销,尽管我还不知道为什么

还有一件有趣的事情:当我切换到x64版本(发行版或调试版)时,这种差异消失了

如果我大胆猜测(完全未经证实),可能是在
B.Add2
a.DoSomething2
中传递64位
作为返回值的成本超过了在32位环境中传递
操作
的成本。在64位环境中,由于
操作
也需要64位,因此这种节省将消失。在这两种配置的发布版本中,传递
long
的成本可能会消失,因为
B.Add2
a.DoSomething2
似乎都是内联的主要候选对象


一个比我更了解这一点的人:请随意完全驳斥我刚才所说的一切。毕竟,我们都是来学习的;)

首先,您对
newa()
的调用正按照当前设置代码的方式进行计时。您需要确保您在发布模式下运行,同时也进行了优化。此外,您还需要考虑JIT——为所有代码路径加上prime,这样您就可以保证在计时之前编译它们(除非您关心启动时间)

当您尝试对大量基本操作(简单加法)计时时,我发现了一个问题。在这种情况下,您无法做出任何明确的结论,因为任何开销都将完全支配您的度量

编辑: 在VS2008中以.NET 3.5为目标的发布模式下,我得到:

1719 : 9999999800000000
1337 : 9999999800000000
这似乎与许多其他答案一致。使用ILDasm为B提供以下IL。添加:

  IL_0000:  ldarg.3
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  add
  IL_0004:  conv.i8
  IL_0005:  callvirt   instance void class [mscorlib]System.Action`1<int64>::Invoke(!0)
  IL_000a:  ret

因此,看起来您几乎只是在计时
加载
callvirt

我对您的代码做了一些更改

  • 已将
    新A()移动到定时部分之前
  • 在timed部分之前添加了预热代码,以获得JIT方法
  • 在定时节和循环之前创建一个
    操作
    引用,这样就不必在每次迭代中创建它。这一次似乎对执行时间有很大影响
这是我在做了上述修改后的结果。vshost列指示代码是否在vshost.exe进程内执行(通过直接从Visual Studio运行)。我使用的是VisualStudio2008,目标是.NET3.5SP1

vshost?   Debug   Release
-------------------------
 YES       6405     3827
          11059     3092

 NO        4214     1691
           4607      811

请注意,根据构建配置和执行环境,如何获得不同的结果。结果是有趣的,如果没有别的。如果我有时间,我可能会编辑我的答案来提供一个理论。

我无法复制这个。对我来说,
DoSomething2
方法的速度大约是前者的两倍。我用x86版本在.NET4.0中测试了这一点。IL还不包含任何内联,而且由于额外的
操作,
DoSomething
速度较慢,这似乎也是合理的。Invoke
方法调用。我也无法复制它。在我的机器上,
DoSomething2
也明显快于
DoSomething
。可能是调试还是发布。对我来说,这两个版本的结果是相反的。对我来说,即使是调试版本
DoSomething2
也更快。然而,基准是比较不同的东西
DoSomething
应该直接调用委托,而不是使其成为公平基准的另一种方法。.net 4.0似乎已经改变了一些事情-我将给出一个结论。在调试构建中获得该结果的原因实际上不是方法调用比调用委托慢。结果是有缺陷的,因为在调试版本中,加载和jitting的总体开销要高得多。要解决此问题,您必须添加一些预热代码,以便在开始测量之前,所有内容都已加载并进行了jitted。您只需将
long
替换为
int
,即可验证您的猜测。但是我更愿意假设64位抖动能够执行一些优化,比如内联,而32位抖动不能
  IL_0000:  ldarg.1
  IL_0001:  ldarg.2
  IL_0002:  add
  IL_0003:  conv.i8
  IL_0004:  ret
vshost?   Debug   Release
-------------------------
 YES       6405     3827
          11059     3092

 NO        4214     1691
           4607      811