`(Enum)(object)e`和`e as Enum`之间的性能,其中e是struct

时间:2015-04-07 19:04:29

标签: c# .net

我很想知道什么更快:

  • (Enum)(object)e

  • e as Enum

e是一个枚举值类型,通过泛型类型参数指定。

我开始使用以下代码对这些进行分析:

using System;
using System.Diagnostics;

public class Program
{
    public static void Main()
    {
        var list = new DateTimeKind[] { DateTimeKind.Local, DateTimeKind.Unspecified, DateTimeKind.Utc };
        var sw = new Stopwatch();

        while (true)
        {
            sw.Restart();
            for (var i = 1; i < 10000000; i++)
                ToSeparatedCommaStringAs<DateTimeKind>(list);
            sw.Stop();
            Console.WriteLine("AS    " + sw.ElapsedTicks);

            sw.Restart();
            for (var i = 1; i < 10000000; i++)
                ToSeparatedCommaStringCast<DateTimeKind>(list);
            sw.Stop();
            Console.WriteLine("CAST  " + sw.ElapsedTicks);

            Console.ReadKey();
        }
    }

    public static string ToSeparatedCommaStringAs<T>(T[] enums)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        var commaString = string.Empty;
        if (!typeof(T).IsEnum)
            throw new ArgumentException("Tipo de enums é inválido");

        foreach (var item in enums)
        {
            Enum enumerador = item as Enum;
            commaString += enumerador.GetStringValue() + ",";
        }

        return commaString.TrimEnd(',');
    }

    public static string ToSeparatedCommaStringCast<T>(T[] enums)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        var commaString = string.Empty;
        if (!typeof(T).IsEnum)
            throw new ArgumentException("Tipo de enums é inválido");

        foreach (var item in enums)
        {
            var enumerador = (Enum)(object)item;
            commaString += enumerador.GetStringValue() + ",";
        }

        return commaString.TrimEnd(',');
    }
}

public static class EnumExt
{
    public static string GetStringValue(this Enum value)
    {
        return "nome"; //só para testar
    }
}

我很惊讶地发现 cast 版本在我的机器上运行得更快。其他人已经测试了不同的结果。有人说as运算符总是更快,有些人说它在运行之间会有所不同。

我在运行时分析了disassemlby窗口,代码不同:

Lines with an `x` were in fact executed:

              Enum enumerador = item as Enum;
x 001E349D  mov         ecx,707E5288h  
x 001E34A2  call        000C30F4  
x 001E34A7  mov         dword ptr [ebp-34h],eax  
x 001E34AA  mov         eax,dword ptr [ebp-34h]  
x 001E34AD  mov         edx,dword ptr [ebp-0Ch]  
x 001E34B0  mov         dword ptr [eax+4],edx  
x 001E34B3  mov         eax,dword ptr [ebp-34h]  
x 001E34B6  mov         dword ptr [ebp-58h],eax  
x 001E34B9  mov         edx,dword ptr [ebp-58h]  
x 001E34BC  mov         ecx,707CF474h  
x 001E34C1  call        715D0260  
x 001E34C6  mov         dword ptr [ebp-24h],eax  



              var enumerador = (Enum)(object)item;
x 001E39F0  mov         ecx,707E5288h  
x 001E39F5  call        000C30F4  
x 001E39FA  mov         dword ptr [ebp-34h],eax  
x 001E39FD  mov         eax,dword ptr [ebp-34h]  
x 001E3A00  mov         edx,dword ptr [ebp-0Ch]  
x 001E3A03  mov         dword ptr [eax+4],edx  
x 001E3A06  mov         eax,dword ptr [ebp-34h]  
x 001E3A09  mov         dword ptr [ebp-38h],eax  
x 001E3A0C  cmp         dword ptr [ebp-38h],0  
x 001E3A10  je          001E3A31  
x 001E3A12  mov         eax,dword ptr [ebp-38h]  
x 001E3A15  cmp         dword ptr [eax],707CF474h  
x 001E3A1B  jne         001E3A22  
  001E3A1D  mov         eax,dword ptr [ebp-38h]  
  001E3A20  jmp         001E3A2F  
x 001E3A22  mov         edx,dword ptr [ebp-38h]  
x 001E3A25  mov         ecx,707CF474h  
x 001E3A2A  call        715D2582  
x 001E3A2F  jmp         001E3A34  
  001E3A31  mov         eax,dword ptr [ebp-38h]  
x 001E3A34  mov         dword ptr [ebp-24h],eax  

为什么结果差异太大......演员版本进行了一个盒子操作,它应该更慢......不是吗?

修改

我现在已经在发布模式下编译并在没有调试器的情况下运行它并提高了进程优先级。 时间是这样的:

AS    21305328
CAST  20655717
AS    20330474
CAST  20714744
AS    23156667
CAST  21187540
AS    19841935
CAST  20838702
AS    20180793
CAST  20498759
AS    20782600
CAST  20454898
AS    19819178
CAST  20294181
AS    20244950
CAST  20241214
AS    20919664
CAST  20771469
AS    19990283
CAST  21707570
AS    19759742
CAST  20667567
AS    20259063
CAST  21602690
AS    20200280
CAST  20668826
AS    20147201
CAST  20048725
AS    19845383
CAST  20226356
AS    20169406
CAST  20401720
AS    20826775
CAST  20114984
AS    20691103
CAST  21552342
AS    20200982
CAST  20858057
AS    19734088
CAST  20266943
AS    19589351
CAST  20477856
AS    19813852
CAST  20350659
AS    20180603
CAST  20307336

问题延续

我发现Enum是引用类型...有人可以确认e as Enum会将e的值设置为(Enum)(object)e,就像Enum一样。由于Enum是一个引用类型,它应该......这是假的唯一方法,以防运行时将Nullable<T>视为特殊内容(就像e as Enum一样)

我之前的想法:

  • (Enum)(object)e不会包装任何东西,而且应该更快
  • e as Enum正在进行一个盒子操作,而且应该更慢

我怀疑这是真的:

  • (Enum)(object)e将执行框操作
  • {{1}}将执行框操作
  • 因为两者都会包装值,两者的性能应该相当

1 个答案:

答案 0 :(得分:2)

正如评论中所说,表现指标和时间彼此如此接近,很难说其中一个铸件比另一个铸件更快。看一下生成的IL代码,可以解释为什么性能对彼此是合理的。

使用(Enum)(object)e投射时,它会被装箱。之后,它使用castclass OpCode。这会将一个对象推入堆栈,然后将其从堆栈中弹出并将其转换为Enum类或任何其他类(如果需要)。新创建的对象被压入堆栈。

使用e as Enum投射时,它也会装箱。之后,它使用isinst OpCode。 isinstcastclass之间的区别在于isinst检查堆栈中推送的对象引用是否可以传递到类中。如果无法传递,则返回null。如果成功,它就像castclass一样。

IL代码isinst

IL_0039: stloc.1
IL_003a: nop
IL_003b: ldloc.1
IL_003c: box !!T
IL_0041: isinst [mscorlib]System.Enum
IL_0046: stloc.2
IL_0047: ldloc.0
IL_0048: ldloc.2

IL代码castclass

IL_0039: stloc.1
IL_003a: nop
IL_003b: ldloc.1
IL_003c: box !!T
IL_0041: castclass [mscorlib]System.Enum
IL_0046: stloc.2
IL_0047: ldloc.0
IL_0048: ldloc.2

希望这对您有所帮助。

参考:https://msdn.microsoft.com/library/system.reflection.emit.opcodes.isinst.aspx https://msdn.microsoft.com/library/system.reflection.emit.opcodes.castclass.aspx