C# 返回值或通过操作返回之间奇怪的性能差异<;T>;参数
我很想知道从方法返回值与通过动作参数返回值之间的性能差异 有一个与此相关的问题 但就我的一生而言,我无法解释为什么返回值要比调用委托返回值慢约30%。是.net抖动(不是编译器..)在我的简单委托中(我认为它没有这样做)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+
类程序
{
静态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? 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