为什么拆箱参考类型在使用辅助方法完成后会表现更差?

时间:2016-01-06 19:35:28

标签: .net performance casting performance-testing

这是我想要做的其他事情的过度简化的例子,但是,现在,请考虑这些投射方法:

public static string StringTryCast(object o)
{
    return o as string;
}

public static T RefTypeTryCast<T>(object o) where T : class
{
    return o as T;
}

当我在50,000,000次迭代的循环中执行它们时,我似乎比执行强制转换的内容要慢得多。以下是我正在进行的四项测试,其评论与他们下面的测试案例相对应。

object BoxedValue = "my string";

//inline trycast
() => { s = BoxedValue as string; }

//method: RefTypeTryCast
() => { s = RefTypeTryCast<string>(BoxedValue); }

//method: StringTryCast
() => { s = StringTryCast(BoxedValue); }

以下是测试结果。我为每种方法运行了五次50,000,000次迭代测试,然后计算了平均值。

inline trycast 50,000,000x...
  368 ms
  370 ms
  374 ms
  380 ms
  380 ms

  374.4 ms average over 5 iterations

method: RefTypeTryCast 50,000,000x...
  1083 ms
  1098 ms
  1100 ms
  1133 ms
  1138 ms

  1110.4 ms average over 5 iterations

method: StringTryCast 50,000,000x...
  477 ms
  478 ms
  487 ms
  489 ms
  493 ms

  484.8 ms average over 5 iterations

At 50,000,000 iterations, inline trycast is...
  1.2949 x Faster than method: StringTryCast
  2.9658 x Faster than method: RefTypeTryCast

我无法理解为什么StringTryCast在辅助方法中进行内联强制转换时会有不同的表现。在方法中添加[MethodImpl(MethodImplOptions.AggressiveInlining)]似乎没有帮助。此外,RefTypeTryCast使用泛型并执行比内联更差3倍。

好像他们都应该表现得相对一样。

编辑:正如评论中所提到的,我使用帮助程序类来运行我的测试。这基本上就是封装逻辑。

Stopwatch sw = new Stopwatch();

for (i = 0; i < 5; i++)
{
    sw.Restart();

    for (int o = 0; o < 50000000; o++)
    {
        Test(); //anon method passed in from lambda expression
    }

    sw.Stop();

    times.Add(i, sw.ElapsedMilliseconds);
}

1 个答案:

答案 0 :(得分:3)

这是发布版本。

dat<-filter(dat,-node_id==9)
dat<-subset(dat,-node_id==9)

如您所见,在泛型函数的情况下还有一条指令:IL code for `StringTryCast` and `RefTypeTryCast`: .method public hidebysig static !!T RefTypeTryCast<class T> (object o) cil managed { IL_0000: ldarg.0 IL_0001: isinst !!T IL_0006: unbox.any !!T IL_000b: ret } .method public hidebysig static string StringTryCast (object o) cil managed { IL_0000: ldarg.0 IL_0001: isinst [mscorlib]System.String IL_0006: ret } 。从https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.unbox_any.aspx我们可以看到它执行了三个操作:将对象推送到堆栈,从堆栈弹出和取消装箱,然后推回堆栈。所以,还有更多的工作要做,因此有更多的时间。

对于内联和unbox.any之间的区别,如果是内联,你有一个推送到堆栈,检查堆栈上的内容是否为字符串,然后从堆栈中弹出。 对于StringTryCast,有推送到堆栈,调用方法将参数推送到堆栈,检查它是否是字符串,返回它将其从堆栈推送到调用者的堆栈然后当它#39 ; s,它再次从堆栈中弹出。 再一次,更多的工作 - &gt;更多时间。