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代码替换了吗?我相信?!
现在,回答你的问题:是的,你是对的.Enum作为Mono的关键,在紧密的循环中会变慢.这是因为Mono在Enums上做拳击,就像我所看到的那样.您可以查看EnumIntEqualityComparer,正如您所看到的,它调用Array.UnsafeMov基本上将一种类型T转换为整数,通过装箱:(int)(object) instance;.这是泛型的"经典"限制,并没有很好的解决方案来解决这个问题.
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觉得很愚蠢.
编写通用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)
现在,解决方案#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.
| 归档时间: |
|
| 查看次数: |
12033 次 |
| 最近记录: |