字典枚举关键性能

Mag*_*son 13 c# mono performance enums dictionary

我关心使用枚举键的通用词典.

如下页所述,使用键的枚举将分配内存:http: //blogs.msdn.com/b/shawnhar/archive/2007/07/02/twin-paths-to-garbage-collector-nirvana.aspx

我已经测试并确认了这个行为,这导致我的项目出现问题.为了便于阅读,我认为使用枚举键是非常有用的,对我来说最好的解决方案是编写一个实现的类IDictionary<TKey, TValue>,它将在内部使用整数键.原因是我不想更改所有现有的词典以使用整数作为键,并进行隐式转换.这将是最好的性能,但它最初会给我很多工作,它会降低可读性.

所以我尝试了几种方法,包括使用GetHashCode(不幸的是分配内存)来构建内部Dictionary<int, TValue>.

所以,把它包装在一个问题中; 任何人都可以想到一个我可以使用的解决方案,以保持可读性Dictionary<SomeEnum, TValue>,同时具有一个Dictionary<int, TValue>

任何建议都非常感谢.

Ert*_*maa 33

问题是拳击.这是将值类型转换为对象的行为,可能或可能不是必需的.

方式Dictionary比较键,基本上是它将使用EqualComparer<T>.Default,并调用GetHashCode()以找到正确的桶,并Equals比较桶中的任何值是否与我们正在寻找的值相等.

好处是:.NET框架具有良好的优化,在这种情况下避免装箱"Enum integers".请参见CreateComparer().你不太可能在这里看到整数和枚举之间的任何差异,作为关键.

在这里要注意:这不是一件容易的事,事实上,如果你深入挖掘,你会得出结论,这场战斗的四分之一是通过CLR"黑客"来实现的.如下所示:

   static internal int UnsafeEnumCast<T>(T val) where T : struct    
    {
        // should be return (int) val; but C# does not allow, runtime 
        // does this magically
        // See getILIntrinsicImplementation for how this happens.  
        throw new InvalidOperationException();
    }
Run Code Online (Sandbox Code Playgroud)

如果泛型具有Enum约束,甚至可能是很长的线条UnsafeEnumCast<T>(T val) where T : Enum->Integer,那么它肯定会更容易 ,但是......它们没有.

您可能想知道,getILIntrinsicImplementation究竟发生了EnumCast什么?我也想知道.在这个正确的时刻不确定如何检查它.它在运行时用特定的IL代码替换了吗?我相信?!

MONO

现在,回答你的问题:是的,你是对的.Enum作为Mono的关键,在紧密的循环中会变慢.这是因为Mono在Enums上做拳击,就像我所看到的那样.您可以查看EnumIntEqualityComparer,正如您所看到的,它调用Array.UnsafeMov基本上将一种类型T转换为整数,通过装箱:(int)(object) instance;.这是泛型的"经典"限制,并没有很好的解决方案来解决这个问题.

解决方案1

EqualityComparer<MyEnum>为你的具体枚举实现一个.这将避免所有的铸造.

public struct MyEnumCOmparer : IEqualityComparer<MyEnum>
{
    public bool Equals(MyEnum x, MyEnum y)
    {
        return x == y;
    }

    public int GetHashCode(MyEnum obj)
    {
        // you need to do some thinking here,
        return (int)obj;
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要做的就是将其传递给您Dictionary:

new Dictionary<MyEnum, int>(new MyEnumComparer());

它有效,它为您提供与整数相同的性能,并避免拳击问题.问题是,这不是通用的,为每个人写这个都会Enum觉得很愚蠢.

解决方案2

编写通用Enum比较器,并使用一些避免拆箱的技巧.我在这里写了一点帮助,

// todo; check if your TEnum is enum && typeCode == TypeCode.Int
struct FastEnumIntEqualityComparer<TEnum> : IEqualityComparer<TEnum> 
    where TEnum : struct
{
    static class BoxAvoidance
    {
        static readonly Func<TEnum, int> _wrapper;

        public static int ToInt(TEnum enu)
        {
            return _wrapper(enu);
        }

        static BoxAvoidance()
        {
            var p = Expression.Parameter(typeof(TEnum), null);
            var c = Expression.ConvertChecked(p, typeof(int));

            _wrapper = Expression.Lambda<Func<TEnum, int>>(c, p).Compile();
        }
    }

    public bool Equals(TEnum firstEnum, TEnum secondEnum)
    {
        return BoxAvoidance.ToInt(firstEnum) == 
            BoxAvoidance.ToInt(secondEnum);
    }

    public int GetHashCode(TEnum firstEnum)
    {
        return BoxAvoidance.ToInt(firstEnum);
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案3

现在,解决方案#2存在一些问题,因为Expression.Compile()在iOS上没有那么着名(没有运行时代码生成),而且有些单声道版本没有?Expression.Compile?? (不确定).

您可以编写简单的IL代码来处理枚举转换并编译它.

.assembly extern mscorlib
{
  .ver 0:0:0:0
}
.assembly 'enum2int'
{
  .hash algorithm 0x00008004
  .ver  0:0:0:0
}

.class public auto ansi beforefieldinit EnumInt32ToInt
    extends [mscorlib]System.Object
{
    .method public hidebysig static int32  Convert<valuetype 
        .ctor ([mscorlib]System.ValueType) TEnum>(!!TEnum 'value') cil managed
    {
      .maxstack  8
      IL_0000:  ldarg.0
      IL_000b:  ret
    }
} 
Run Code Online (Sandbox Code Playgroud)

要将其编译为程序集,您必须调用:

ilasm enum2int.il /dll 其中enum2int.il是包含IL的文本文件.

您现在可以引用给定的assembly(enum2int.dll)并调用静态方法,如下所示:

struct FastEnumIntEqualityComparer<TEnum> : IEqualityComparer<TEnum> 
    where TEnum : struct
{
    int ToInt(TEnum en)
    {
        return EnumInt32ToInt.Convert(en);
    }

    public bool Equals(TEnum firstEnum, TEnum secondEnum)
    {
        return ToInt(firstEnum) == ToInt(secondEnum);
    }

    public int GetHashCode(TEnum firstEnum)
    {
        return ToInt(firstEnum);
    }
}
Run Code Online (Sandbox Code Playgroud)

它似乎是杀手级代码,但它避免了拳击,它应该给你更好的表现Mono.