我曾经认为在.NET中转换(而非将类型转换)是很便宜和快速的。然而,对于数组,事实似乎并非如此。我试图做一个非常简单的转换,将T1[]作为T2[]进行转换,其中T1:T2。
有三种方法可以做到这一点,我称之为以下三种方式:
滴定法(Dropcasting)比任何一个类型转换运算符快约18倍。为什么数组类型转换这么慢?对于像字符串=>对象这样的普通对象,差异要小得多。
以下是基准代码:
在有人问之前,请编辑int []-> uint []等事物,这同样适用于clr规范,应该在没有转换的情况下进行转换。
有三种方法可以做到这一点,我称之为以下三种方式:
DropCasting: T2[] array2 = array;
CastClass: (T2[])array;
IsInst: array as T2[];
我创建了一些方法来实现这个功能,但不幸的是,C# 似乎会根据此是否为泛型来创建一些相当奇怪的代码。(如果它是泛型,则 DropCasting 使用 castclass 操作符。在两种情况下,都拒绝发出 'as' 运算符,当 T1:T2 时)
无论如何,我编写了一些动态方法,并进行了测试,得到了一些惊人的结果(string[] => object[]):
DropCast : 223ms
IsInst : 3648ms
CastClass: 3732ms
滴定法(Dropcasting)比任何一个类型转换运算符快约18倍。为什么数组类型转换这么慢?对于像字符串=>对象这样的普通对象,差异要小得多。
DropCast : 386ms
IsInst : 611ms
CastClass: 519ms
以下是基准代码:
class Program
{
static readonly String[] strings = Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray();
static Func<string[], object[]> Dropcast = new Func<Func<string[], object[]>>(
() =>
{
var method = new DynamicMethod("DropCast", typeof(object[]), new[] { typeof(object), typeof(string[]) },true);
var ilgen = method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>;
})();
static Func<string[], object[]> CastClass = new Func<Func<string[], object[]>>(
() =>
{
var method = new DynamicMethod("CastClass", typeof(object[]), new[] { typeof(object), typeof(string[]) },true);
var ilgen = method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Castclass, typeof(object[]));
ilgen.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>;
})();
static Func<string[], object[]> IsInst = new Func<Func<string[], object[]>>(
() =>
{
var method = new DynamicMethod("IsInst", typeof(object[]), new[] { typeof(object), typeof(string[]) },true);
var ilgen = method.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Isinst, typeof(object[]));
ilgen.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>;
})();
static Func<string[], object[]>[] Tests = new Func<string[], object[]>[]{
Dropcast,
IsInst,
CastClass
};
static void Main(string[] args)
{
int maxMethodLength = Tests.Select(x => GetMethodName(x.Method).Length).Max();
RunTests(1, false, maxMethodLength);
RunTests(100000000, true, maxMethodLength);
}
static string GetMethodName(MethodInfo method)
{
return method.IsGenericMethod ?
string.Format(@"{0}<{1}>", method.Name, string.Join<Type>(",", method.GetGenericArguments())) : method.Name;
}
static void RunTests(int count, bool displayResults, int maxLength)
{
foreach (var action in Tests)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
action(strings);
}
sw.Stop();
if (displayResults)
{
Console.WriteLine("{0}: {1}ms", GetMethodName(action.Method).PadRight(maxLength),
((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
}
在有人问之前,请编辑int []-> uint []等事物,这同样适用于clr规范,应该在没有转换的情况下进行转换。
()=>strings as object[];
编译器将删除as
方法。动态方法创建仅在程序的.cctor
中运行一次。之后,每个方法都只是IL blob。此外,我为每个动态方法添加了一个“实例”(对象参数),只是为了避免在使用静态方法的委托时进行thunk shuffle。 - Michael B