Why is casting arrays (vectors) so slow?

I got the impression that in .NET casting (not convertting) is very cheap and fast. However, this does not look like an array. I am trying to make a very simple throw here, take T1 [] and throw as T2 []. where T1: T2.

There are 3 ways to do this, and I call them the following:

DropCasting: T2[] array2 = array; CastClass: (T2[])array; IsInst: array as T2[]; 

And I created methods for this, unfortunately, C # seems to be creating some rather strange code depending on whether this is common or not. (If its generic DropCasting uses the castclass operator. And in both cases refuse to give out an "how" operator when T1: T2.

In any case, I wrote some dynamic methods, and I checked it for some unexpected results (line [] => object []):

 DropCast : 223ms IsInst : 3648ms CastClass: 3732ms 

Dropcasting was ~ 18 times faster than any of the broadcast operators. Why is arrays too slow? For ordinary objects, such as string => object, the difference was much less serious.

 DropCast : 386ms IsInst : 611ms CastClass: 519ms 

Code below:

 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(); } } } 

Edit before anyone asks the same thing is true for things like int [] → uint [], which clr specs should be distinguished without conversion.

+8
arrays c #
source share
2 answers

Because you produce arrays.

The difference between the three pieces of IL code is that the last two add the operation IsInst and CastClass. Very little is known about types, so the CLR should check to see if this is a valid operation. It takes time.

The small difference between CastClass and IsInst is due to the fact that CastClass first performs a null check and is immediately deleted if the argument is null.

I suspect that the slowdown is due to what you produce between arrays. It may take more work to make sure the array array is valid. You may need to look at each element to see if it can be applied to the type of the target element. Therefore, I would suggest that instead of doing all of this in Inline machine code, JIT emits a validation function call.

In fact, if you are doing a performance analysis, you can see that this is really what is happening. Almost 90% of the time is spent on a function called "JIT_ChkCastArray".

0
source share

It seems to me that casting will be (almost) as expensive as the as operator. In both scenarios, it is necessary to check the runtime of the object and determine whether it is compatible with the type of target. Validation is necessary to allow casting operations to throw an InvalidCastException , if necessary.

In other words, the as operator is an act of actuation — it just has the power to allow exceptions to be thrown without exception (by returning null). This can also be done using a combination of the is statement and cast, but this will double the workload.

0
source share

All Articles