C# 具有单个对象字段的结构如何比原始对象更快?
我有一个C# 具有单个对象字段的结构如何比原始对象更快?,c#,.net,performance,C#,.net,Performance,我有一个struct,它保存一个对象字段,以便更轻松地使用该对象。我想测试性能(我预计性能会下降),但我得到了非常令人惊讶的结果带有结构的版本实际上更快: 不带盒子:8.08秒 带方框:7.76秒 这怎么可能? 下面是复制结果的完整测试代码 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerService
struct
,它保存一个对象
字段,以便更轻松地使用该对象。我想测试性能(我预计性能会下降),但我得到了非常令人惊讶的结果带有结构的版本实际上更快:
不带盒子:8.08秒
带方框:7.76秒
这怎么可能?
下面是复制结果的完整测试代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication68
{
partial class Program
{
private const int Iterations = 100000000;
static void Main(string[] args)
{
// Force JIT compilation.
TimeWithoutBox(new MyObject());
TimeWithoutBox(7);
TimeBox(new MyObject());
TimeBox(7);
// The tests.
var withoutBox = new TimeSpan();
var box = new TimeSpan();
for (int i = 0; i < 10; i++)
{
withoutBox += TimeWithoutBox(new MyObject());
withoutBox += TimeWithoutBox(7);
box += TimeBox(new MyObject());
box += TimeBox(7);
}
Console.WriteLine("Without box: " + withoutBox);
Console.WriteLine("With box: " + box);
Console.ReadLine();
}
private static TimeSpan TimeBox(object value)
{
var box = new MyBox(value);
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
TestBox(box);
}
return stopwatch.Elapsed;
}
private static TimeSpan TimeWithoutBox(object value)
{
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
TestWithoutBox(value);
}
return stopwatch.Elapsed;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TestBox(MyBox box)
{
if (box.IsDouble)
TakeDouble((double)box.Value);
else if (box.IsObject)
TakeObject((MyObject)box.Value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TestWithoutBox(object box)
{
if (box.GetType() == typeof(double))
TakeDouble((double)box);
else if (box.GetType() == typeof(MyObject))
TakeObject((MyObject)box);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TakeDouble(double value)
{
// Empty method to force consuming the cast.
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TakeObject(MyObject value)
{
// Empty method to force consuming the cast.
}
}
struct MyBox
{
private readonly object _value;
public object Value
{
get { return _value; }
}
public MyBox(object value)
{
_value = value;
}
public bool IsDouble
{
get { return _value.GetType() == typeof(double); }
}
public bool IsObject
{
get { return _value.GetType() == typeof(MyObject); }
}
}
class MyObject
{
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
使用System.Runtime.CompilerServices;
使用系统文本;
使用System.Threading.Tasks;
命名空间控制台应用程序68
{
部分类程序
{
私有常量int迭代次数=100000000;
静态void Main(字符串[]参数)
{
//强制JIT编译。
TimeWithoutBox(新的MyObject());
不带盒子的时间(7);
TimeBox(新的MyObject());
计时器(7);
//测试。
var withoutBox=new TimeSpan();
变量框=新的时间跨度();
对于(int i=0;i<10;i++)
{
withoutBox+=时间withoutBox(新的MyObject());
不带盒子+=不带盒子的时间(7);
box+=时间框(新的MyObject());
盒子+=时间盒(7);
}
控制台写入线(“无框:+无框”);
Console.WriteLine(“带框:+box”);
Console.ReadLine();
}
专用静态时间跨度时间框(对象值)
{
变量框=新的MyBox(值);
var stopwatch=stopwatch.StartNew();
对于(int i=0;i
编辑:
我已将IsDouble
和IsObject
测试更改为与其他测试具有相同的语句。我已经重新执行了应用程序,结果时间完全相同
EDIT2:
这段代码是使用版本进行测试的,编译为32位,没有附加调试程序。NET 4.5和Visual Studio 2012。根据64位编译它会得到截然不同的结果;在我的机器上:
不带方框:8.23秒
带方框:16.99秒
我复制了准确的代码,在没有调试器的情况下运行了它的发行版(这两个都很重要!),并在x64上运行。结果:
Without box: 00:00:07.9650541
With box: 00:00:16.0958162
将测试更改为:
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TestBox(MyBox box)
{
if (box.Value.GetType() == typeof(double))
TakeDouble((double)box.Value);
else if (box.Value.GetType() == typeof(MyObject))
TakeObject((MyObject)box.Value);
}
使运行时间几乎相等:
Without box: 00:00:07.9488281
With box: 00:00:08.6084029
为什么??因为JIT决定不内联IsDouble
,手动内联有帮助。这很奇怪,因为它是一个很小的函数。第13行的调用
就是这个调用
现在为什么还有一些性能差异?NET JIT不是最好的编译器。。。可能有些说明有点不同。您可以通过比较两个版本的反汇编来找到答案。我没有时间,因为我预计两者之间的差别会很小
我希望C编译器能做到这一点。该结构的行为应该类似于它所包含的单个
对象成员。小方法应该内联。这在当今的编译器技术中是绝对可行的。让我们希望下一代JIT和NGEN能够做到这一点。目前正在开发一种新的JIT(RyuJIT),他们正在将优化从VC后端转移到NGEN(最近宣布)。1亿次迭代中的32ms差异?谁在乎呢。你怎么知道你不是在间接测试IsDouble
、IsObject
、GetType()
和typeof()
的效率?你使用过哪个visual studio版本?你重复了测试吗?您是否尝试过不同的构建——发布/调试。。。。还有一点好奇。。。。。你还试过单声道吗?@Dan-o:你说得很对,但我觉得这很奇怪,我想知道发生了什么。@Dan-o:顺便说一句,这不是32毫秒,而是320毫秒。4%的差异在我看来是巨大的。我做这些测试的全部原因