typeof(T) vs. TypeOf⟨T⟩

в 9:35, , рубрики: .net, C#, Программирование, Промышленное программирование

Иногда рефлексивные вызовы дороги в терминах производительности и не могут быть опущены.

В этой статье представлены паттерны, позволяющие существенно повысить производительность множественных рефлексивных вызовов посредством техники виртуализации (кэширования) результатов.

image


Рассмотрим следующий метод:

static void AnyPerformanceCriticalMethod()
{
	var anyTypeName = typeof(AnyType).Name;
	/* ... using of anyTypeName ... */
}

Важный паттерн в подобных случаях — это кэширование локальной переменной в качестве статической, например:

/* much better! */
static readonly string AnyTypeName = typeof(AnyType).Name;

static void AnyPerformanceCriticalMethod()
{
	/* ... using of AnyTypeName ... */
}

Но как поступить в случае обобщённого (generic) метода?

static void AnyPerformanceCriticalMethod<T>()
{
	var anyTypeName = typeof(T).Name;
	/* ... using of anyTypeName ... */
}

Существует практичное обобщённое решение, взгляните.

TypeOf.cs

public static class TypeOf<T>
{
	/* Important! Should be a readonly variable for best performance */ 
	public static readonly Type Raw = typeof(T);
	
	public static readonly string Name = Raw.Name;
	public static readonly Assembly Assembly = Raw.Assembly;
	public static readonly bool IsValueType = Raw.IsValueType;
	/* etc. */
}

static void AnyPerformanceCriticalMethod<T>()
{
	var anyTypeName = TypeOf<T>.Name;
	/* ... using of anyTypeName ... */
}

*Примечательно, что до момента добавления обобщённых классов и методов, C# уже имел поддержку ряда обобщённых операторов: `typeof`, `is`, `as`

Что насчёт другого сценария?

static void AnyPerformanceCriticalMethod(object item)
{
	var itemTypeName = o.GetType().Name;
	/* ... using of anyTypeName ... */
}

Можем попробовать.

RipeType.cs

public class RipeType
{
	internal RipeType(Type raw)
	{
		Raw = raw;
		
		Name = raw.Name;
		Assembly = raw.Assembly;
		IsValueType = raw.IsValueType;
		/* etc. */
	}

	public static Type Raw { get; }
	
	public string Name { get; }
	public Assembly Assembly { get; }
	public bool IsValueType { get; }
	/* etc. */
}

TypeOf.cs

public static class TypeOf
{
	public static readonly Dictionary<Type, RipeType> RawToRipe = new Dictionary<Type, RipeType>();

	public static RipeType ToRipeType(this Type type) =>
		RawToRipe.TryGetValue(type, out var typeData)
			? typeData
			: Lock.Invoke(() => RawToRipe.TryGetValue(type, out typeData)
				? typeData // may catch item created into a different thread
				: RawToRipe[type] = new RipeType(type));
				
	public static RipeType GetRipeType(this object o) => o.GetType().ToRipeType();
}

Lock.cs

public static class Lock
{
	public static TResult Invoke<TResult>(Func<TResult> func)
	{
		lock (func) return func();
	}
}

Итак, теперь можно использовать:

static void AnyPerformanceCriticalMethod(object item)
{
	var itemTypeName = o.GetRipeType().Name;
	/* ... using of anyTypeName ... */
}

Что насчёт недостатков `TypeOf` паттерна?
* `typeof(List<>)` допустимо
* `TypeOf<List<>>` не допустимо

Как решить?

var listAssemby = TypeOf.List.Assembly;

где

public static class TypeOf
{
	/* ... */

	public static readonly RipeType Object = typeof(object).ToRipeType();
	public static readonly RipeType String = typeof(string).ToRipeType();
	public static readonly RipeType Array = typeof(Array).ToRipeType();
	public static readonly RipeType Type = typeof(Type).ToRipeType();
	public static readonly RipeType List = typeof(List<>).ToRipeType();
	public static readonly RipeType IList = typeof(IList<>).ToRipeType();
	public static readonly RipeType Dictionary = typeof(Dictionary<,>).ToRipeType();
	public static readonly RipeType IDictionary = typeof(IDictionary<,>).ToRipeType();
	public static readonly RipeType KeyValuePair = typeof(KeyValuePair<,>).ToRipeType();
	public static readonly RipeType DictionaryEntry = typeof(DictionaryEntry).ToRipeType();
}

Самое время для бенчмарков!

typeof vs. TypeOf [BenchmarkDotNet - code]

[
	CoreJob,
	ClrJob,
	MonoJob("Mono", @"C:Program FilesMonobinmono.exe")
]
public class TypeOfBenchmarks
{
	[Benchmark] public Type typeof_int() => typeof(int);
	[Benchmark] public Type TypeOf_int() => TypeOf<int>.Raw;
	[Benchmark] public Type typeof_string() => typeof(string);
	[Benchmark] public Type TypeOf_string() => TypeOf<string>.Raw;

	[Benchmark] public string typeof_int_Name() => typeof(int).Name;
	[Benchmark] public string TypeOf_int_Name() => TypeOf<int>.Name;
	[Benchmark] public string typeof_string_Name() => typeof(string).Name;
	[Benchmark] public string TypeOf_string_Name() => TypeOf<string>.Name;
	
	[Benchmark] public Assembly typeof_int_Assembly() => typeof(int).Assembly;
	[Benchmark] public Assembly TypeOf_int_Assembly() => TypeOf<int>.Assembly;
	[Benchmark] public Assembly typeof_string_Assembly() => typeof(string).Assembly;
	[Benchmark] public Assembly TypeOf_string_Assembly() => TypeOf<string>.Assembly;
	
	[Benchmark] public bool typeof_int_IsValueType() => typeof(int).IsValueType;
	[Benchmark] public bool TypeOf_int_IsValueType() => TypeOf<int>.IsValueType;
	[Benchmark] public bool typeof_string_IsValueType() => typeof(string).IsValueType;
	[Benchmark] public bool TypeOf_string_IsValueType() => TypeOf<string>.IsValueType;
}

typeof vs. TypeOf [BenchmarkDotNet - results]

Total time: 00:23:34 (1414.47 sec)

// * Summary *

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
Intel Core i7-3517U CPU 1.90GHz (Ivy Bridge), 1 CPU, 4 logical and 2 physical cores
Frequency=2338440 Hz, Resolution=427.6355 ns, Timer=TSC
.NET Core SDK=2.1.302
  [Host] : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT
  Clr    : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3160.0
  Core   : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT
  Mono   : Mono 5.12.0 (Visual Studio), 64bit 


                    Method |  Job | Runtime |        Mean |      Error |     StdDev |
-------------------------- |----- |-------- |------------:|-----------:|-----------:|
                typeof_int |  Clr |     Clr |   3.2686 ns |  0.0490 ns |  0.0434 ns |
                TypeOf_int |  Clr |     Clr |   0.0495 ns |  0.1124 ns |  0.0939 ns |
             typeof_string |  Clr |     Clr |   3.1980 ns |  0.0288 ns |  0.0270 ns |
             TypeOf_string |  Clr |     Clr |   0.0520 ns |  0.0773 ns |  0.0723 ns |
           typeof_int_Name |  Clr |     Clr |  19.4201 ns |  0.1220 ns |  0.1141 ns |
           TypeOf_int_Name |  Clr |     Clr |   0.0082 ns |  0.0169 ns |  0.0159 ns |
        typeof_string_Name |  Clr |     Clr |  19.5041 ns |  0.1397 ns |  0.1090 ns |
        TypeOf_string_Name |  Clr |     Clr |   0.0007 ns |  0.0031 ns |  0.0028 ns |
       typeof_int_Assembly |  Clr |     Clr |  33.8565 ns |  0.6931 ns |  0.5788 ns |
       TypeOf_int_Assembly |  Clr |     Clr |   0.0034 ns |  0.0130 ns |  0.0115 ns |
    typeof_string_Assembly |  Clr |     Clr |  33.9922 ns |  0.2244 ns |  0.1989 ns |
    TypeOf_string_Assembly |  Clr |     Clr |   0.0001 ns |  0.0004 ns |  0.0003 ns |
    typeof_int_IsValueType |  Clr |     Clr |  56.1685 ns |  0.3858 ns |  0.3420 ns |
    TypeOf_int_IsValueType |  Clr |     Clr |   0.4990 ns |  0.0141 ns |  0.0132 ns |
 typeof_string_IsValueType |  Clr |     Clr |  94.0358 ns |  0.4386 ns |  0.3662 ns |
 TypeOf_string_IsValueType |  Clr |     Clr |   0.4960 ns |  0.0109 ns |  0.0102 ns |

                typeof_int | Core |    Core |   1.9114 ns |  0.0527 ns |  0.0493 ns |
                TypeOf_int | Core |    Core |   6.1310 ns |  0.0494 ns |  0.0462 ns |
             typeof_string | Core |    Core |   2.2120 ns |  0.0522 ns |  0.0436 ns |
             TypeOf_string | Core |    Core |   6.1174 ns |  0.0481 ns |  0.0401 ns |
           typeof_int_Name | Core |    Core |  19.5100 ns |  0.1998 ns |  0.1771 ns |
           TypeOf_int_Name | Core |    Core |   6.1495 ns |  0.0829 ns |  0.0735 ns |
        typeof_string_Name | Core |    Core |  19.3662 ns |  0.0895 ns |  0.0793 ns |
        TypeOf_string_Name | Core |    Core |   6.1589 ns |  0.0314 ns |  0.0278 ns |
       typeof_int_Assembly | Core |    Core |  23.4876 ns |  0.1885 ns |  0.1763 ns |
       TypeOf_int_Assembly | Core |    Core |   6.1362 ns |  0.0415 ns |  0.0388 ns |
    typeof_string_Assembly | Core |    Core |  25.5613 ns |  0.2293 ns |  0.2033 ns |
    TypeOf_string_Assembly | Core |    Core |   6.1082 ns |  0.0352 ns |  0.0312 ns |
    typeof_int_IsValueType | Core |    Core |  49.8048 ns |  0.2305 ns |  0.1925 ns |
    TypeOf_int_IsValueType | Core |    Core |   7.1171 ns |  0.0477 ns |  0.0423 ns |
 typeof_string_IsValueType | Core |    Core |  84.8155 ns |  0.7962 ns |  0.7058 ns |
 TypeOf_string_IsValueType | Core |    Core |   7.0987 ns |  0.0521 ns |  0.0487 ns |

                typeof_int | Mono |    Mono |   0.0725 ns |  0.0229 ns |  0.0214 ns |
                TypeOf_int | Mono |    Mono |   3.0123 ns |  0.0652 ns |  0.0610 ns |
             typeof_string | Mono |    Mono |   0.0185 ns |  0.0206 ns |  0.0193 ns |
             TypeOf_string | Mono |    Mono |   9.3828 ns |  0.0863 ns |  0.0765 ns |
           typeof_int_Name | Mono |    Mono | 429.8195 ns |  4.4049 ns |  3.6783 ns |
           TypeOf_int_Name | Mono |    Mono |   2.3856 ns |  0.1608 ns |  0.1426 ns |
        typeof_string_Name | Mono |    Mono | 439.3774 ns |  1.2985 ns |  1.2146 ns |
        TypeOf_string_Name | Mono |    Mono |   8.8580 ns |  0.0728 ns |  0.0646 ns |
       typeof_int_Assembly | Mono |    Mono | 223.5933 ns |  0.6152 ns |  0.5454 ns |
       TypeOf_int_Assembly | Mono |    Mono |   2.2587 ns |  0.0494 ns |  0.0462 ns |
    typeof_string_Assembly | Mono |    Mono | 227.3259 ns |  0.6448 ns |  0.5716 ns |
    TypeOf_string_Assembly | Mono |    Mono |   9.3276 ns |  0.1215 ns |  0.1136 ns |
    typeof_int_IsValueType | Mono |    Mono | 490.2376 ns |  4.3860 ns |  4.1027 ns |
    TypeOf_int_IsValueType | Mono |    Mono |   3.1849 ns |  0.0145 ns |  0.0129 ns |
 typeof_string_IsValueType | Mono |    Mono | 997.4254 ns | 11.6159 ns | 10.8655 ns |
 TypeOf_string_IsValueType | Mono |    Mono |   9.6504 ns |  0.0354 ns |  0.0331 ns |

Type vs. RipeType [BenchmarkDotNet - code]

[
	CoreJob,
	ClrJob,
	MonoJob("Mono", @"C:Program FilesMonobinmono.exe")
]
public class RipeTypeBenchmarks
{
	static object o = new object();
	readonly Type rawType = o.GetType();
	readonly RipeType ripeType = o.GetRipeType();
	
	[Benchmark] public string RawType_Name() => rawType.Name;
	[Benchmark] public string RipeType_Name() => ripeType.Name;
	[Benchmark] public string GetRawType_Name() => o.GetType().Name;
	[Benchmark] public string GetRipeType_Name() => o.GetRipeType().Name;

	[Benchmark] public Assembly RawType_Assembly() => rawType.Assembly;
	[Benchmark] public Assembly RipeType_Assembly() => ripeType.Assembly;
	[Benchmark] public Assembly GetRawType_Assembly() => o.GetType().Assembly;
	[Benchmark] public Assembly GetRipeType_Assembly() => o.GetRipeType().Assembly;

	[Benchmark] public bool RawType_IsValueType() => rawType.IsValueType;
	[Benchmark] public bool RipeType_IsValueType() => ripeType.IsValueType;
	[Benchmark] public bool GetRawType_IsValueType() => o.GetType().IsValueType;
	[Benchmark] public bool GetRipeType_IsValueType() => o.GetRipeType().IsValueType;
}

Type vs. RipeType [BenchmarkDotNet - results]

Total time: 00:14:59 (899.57 sec)

// * Summary *

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
Intel Core i7-3517U CPU 1.90GHz (Ivy Bridge), 1 CPU, 4 logical and 2 physical cores
Frequency=2338440 Hz, Resolution=427.6355 ns, Timer=TSC
.NET Core SDK=2.1.302
  [Host] : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT
  Clr    : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3160.0
  Core   : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT
  Mono   : Mono 5.12.0 (Visual Studio), 64bit


                  Method |  Job | Runtime |        Mean |     Error |    StdDev |
------------------------ |----- |-------- |------------:|----------:|----------:|
            RawType_Name |  Clr |     Clr |  10.2733 ns | 0.1112 ns | 0.1040 ns |
           RipeType_Name |  Clr |     Clr |   0.0164 ns | 0.0220 ns | 0.0206 ns |
         GetRawType_Name |  Clr |     Clr |  15.3661 ns | 0.4064 ns | 0.7431 ns |
        GetRipeType_Name |  Clr |     Clr |  43.3530 ns | 0.4160 ns | 0.3474 ns |
        RawType_Assembly |  Clr |     Clr |  19.8898 ns | 0.1967 ns | 0.1840 ns |
       RipeType_Assembly |  Clr |     Clr |   0.0002 ns | 0.0010 ns | 0.0009 ns |
     GetRawType_Assembly |  Clr |     Clr |  22.7084 ns | 0.1512 ns | 0.1340 ns |
    GetRipeType_Assembly |  Clr |     Clr |  43.1685 ns | 0.3532 ns | 0.3304 ns |
     RawType_IsValueType |  Clr |     Clr |  35.7668 ns | 0.2840 ns | 0.2517 ns |
    RipeType_IsValueType |  Clr |     Clr |   0.0005 ns | 0.0020 ns | 0.0018 ns |
  GetRawType_IsValueType |  Clr |     Clr |  39.6176 ns | 0.2465 ns | 0.2306 ns |
 GetRipeType_IsValueType |  Clr |     Clr |  43.4645 ns | 0.9240 ns | 0.8643 ns |
 
            RawType_Name | Core |    Core |  10.7102 ns | 0.1705 ns | 0.1511 ns |
           RipeType_Name | Core |    Core |   0.0075 ns | 0.0154 ns | 0.0144 ns |
         GetRawType_Name | Core |    Core |  12.8294 ns | 0.0698 ns | 0.0653 ns |
        GetRipeType_Name | Core |    Core |  38.7723 ns | 0.2665 ns | 0.2493 ns |
        RawType_Assembly | Core |    Core |  13.1644 ns | 0.0729 ns | 0.0682 ns |
       RipeType_Assembly | Core |    Core |   0.0174 ns | 0.0207 ns | 0.0194 ns |
     GetRawType_Assembly | Core |    Core |  15.3733 ns | 0.1252 ns | 0.1110 ns |
    GetRipeType_Assembly | Core |    Core |  38.7863 ns | 0.3133 ns | 0.2616 ns |
     RawType_IsValueType | Core |    Core |  32.9788 ns | 0.4456 ns | 0.3721 ns |
    RipeType_IsValueType | Core |    Core |   0.0365 ns | 0.0128 ns | 0.0107 ns |
  GetRawType_IsValueType | Core |    Core |  35.4362 ns | 0.2927 ns | 0.2595 ns |
 GetRipeType_IsValueType | Core |    Core |  39.8377 ns | 0.2895 ns | 0.2708 ns |
 
            RawType_Name | Mono |    Mono | 287.4362 ns | 2.3812 ns | 2.2274 ns |
           RipeType_Name | Mono |    Mono |   0.4614 ns | 0.0320 ns | 0.0299 ns |
         GetRawType_Name | Mono |    Mono | 288.2094 ns | 2.2540 ns | 2.1084 ns |
        GetRipeType_Name | Mono |    Mono |  54.3390 ns | 0.2807 ns | 0.2625 ns |
        RawType_Assembly | Mono |    Mono | 143.6474 ns | 0.7524 ns | 0.7038 ns |
       RipeType_Assembly | Mono |    Mono |   0.7015 ns | 0.0261 ns | 0.0244 ns |
     GetRawType_Assembly | Mono |    Mono | 144.0314 ns | 3.2279 ns | 3.0194 ns |
    GetRipeType_Assembly | Mono |    Mono |  54.5511 ns | 0.2955 ns | 0.2619 ns |
     RawType_IsValueType | Mono |    Mono | 277.4973 ns | 1.4938 ns | 1.3242 ns |
    RipeType_IsValueType | Mono |    Mono |   0.5206 ns | 0.0176 ns | 0.0156 ns |
  GetRawType_IsValueType | Mono |    Mono | 280.7464 ns | 2.1995 ns | 1.8367 ns |
 GetRipeType_IsValueType | Mono |    Mono |  58.5908 ns | 0.1690 ns | 0.1498 ns |

Manual benchmarks - Code (much faster runs)

using System;
using System.Diagnostics;
using System.Linq;
using Ace.Base.Benchmarking.Benchmarks;
using BenchmarkDotNet.Running;

namespace Ace.Base.Benchmarking
{
    static class Program
    {
        private const long WarmRunsCount = 1000;
        private const long HotRunsCount = 10000000; // 10 000 000

        static void Main()
        {
            //BenchmarkRunner.Run<TypeOfBenchmarks>();
            //BenchmarkRunner.Run<RipeTypeBenchmarks>();
            
            TypeofVsTypeOf();
            RawTypeVsRipeType();

            Console.ReadKey();
        }

        static void RawTypeVsRipeType()
        {
            Console.WriteLine();
            Console.WriteLine($"Count of warm iterations: {WarmRunsCount}");
            Console.WriteLine($"Count of hot iterations: {HotRunsCount}");
            Console.WriteLine();
            var o = new object();
            var rawType = o.GetType();
            var ripeType = o.GetRipeType();

            RunBenchmarks(
                (() => rawType.Name, "() => rawType.Name"),
                (() => ripeType.Name, "() => ripeType.Name"),
                (() => o.GetType().Name, "() => o.GetType().Name"),
                (() => o.GetRipeType().Name, "() => o.GetRipeType().Name")
            );

            Console.WriteLine();

            RunBenchmarks(
                (() => rawType.Assembly, "() => rawType.Assembly"),
                (() => ripeType.Assembly, "() => ripeType.Assembly"),
                (() => o.GetType().Assembly, "() => o.GetType().Assembly"),
                (() => o.GetRipeType().Assembly, "() => o.GetRipeType().Assembly")
            );

            Console.WriteLine();

            RunBenchmarks(
                (() => rawType.IsValueType, "() => rawType.IsValueType"),
                (() => ripeType.IsValueType, "() => ripeType.IsValueType"),
                (() => o.GetType().IsValueType, "() => o.GetType().IsValueType"),
                (() => o.GetRipeType().IsValueType, "() => o.GetRipeType().IsValueType")
            );
        }

        static void TypeofVsTypeOf()
        {
            Console.WriteLine($"Count of warm iterations: {WarmRunsCount}");
            Console.WriteLine($"Count of hot iterations: {HotRunsCount}");
            Console.WriteLine();

            RunBenchmarks(
                (() => typeof(int), "() => typeof(int)"),
                (() => TypeOf<int>.Raw, "() => TypeOf<int>.Raw"),
                (() => typeof(string), "() => typeof(string)"),
                (() => TypeOf<string>.Raw, "() => TypeOf<string>.Raw")
            );

            Console.WriteLine();

            RunBenchmarks(
                (() => typeof(int).Name, "() => typeof(int).Name"),
                (() => TypeOf<int>.Name, "() => TypeOf<int>.Name"),
                (() => typeof(string).Name, "() => typeof(string).Name"),
                (() => TypeOf<string>.Name, "() => TypeOf<string>.Name")
            );

            Console.WriteLine();

            RunBenchmarks(
                (() => typeof(int).Assembly, "() => typeof(int).Assembly"),
                (() => TypeOf<int>.Assembly, "() => TypeOf<int>.Assembly"),
                (() => typeof(string).Assembly, "() => typeof(string).Assembly"),
                (() => TypeOf<string>.Assembly, "() => TypeOf<string>.Assembly")
            );

            Console.WriteLine();

            RunBenchmarks(
                (() => typeof(int).IsValueType, "() => typeof(int).IsValueType"),
                (() => TypeOf<int>.IsValueType, "() => TypeOf<int>.IsValueType"),
                (() => typeof(string).IsValueType, "() => typeof(string).IsValueType"),
                (() => TypeOf<string>.IsValueType, "() => TypeOf<string>.IsValueType")
            );
        }

        static void RunBenchmarks<T>(params (Func<T> Func, string StringRepresentation)[] funcAndViewTuples) =>
            funcAndViewTuples
                .Select(t => (
                    BenchmarkResults: t.Func.InvokeBenchmark(HotRunsCount, WarmRunsCount),
                    StringRepresentation: t.StringRepresentation))
                .ToList().ForEach(t =>
                    Console.WriteLine(
                        $"{t.StringRepresentation}t{t.BenchmarkResults.Result}t{t.BenchmarkResults.ElapsedMilliseconds} (ms)"));

        static (Func<T> Func, long ElapsedMilliseconds, T Result) InvokeBenchmark<T>(this Func<T> func,
            long hotRunsCount, long warmRunsCount)
        {
            var stopwatch = new Stopwatch();
            var result = default(T);

            for (var i = 0L; i < warmRunsCount; i++)
                result = func();

            stopwatch.Start();
            for (var i = 0L; i < hotRunsCount; i++)
                result = func();

            stopwatch.Stop();
            return (func, stopwatch.ElapsedMilliseconds, result);
        }
    }
}

Manual benchmarks - Results on Core CLR


Count of warm iterations: 1000
Count of hot iterations: 10000000

() => typeof(int)       System.Int32    70 (ms)
() => TypeOf<int>.Raw   System.Int32    106 (ms)
() => typeof(string)    System.String   70 (ms)
() => TypeOf<string>.Raw        System.String   101 (ms)

() => typeof(int).Name  Int32   249 (ms)
() => TypeOf<int>.Name  Int32   42 (ms)
() => typeof(string).Name       String  245 (ms)
() => TypeOf<string>.Name       String  48 (ms)

() => typeof(int).Assembly      System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       285 (ms)
() => TypeOf<int>.Assembly      System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       42 (ms)
() => typeof(string).Assembly   System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       340 (ms)
() => TypeOf<string>.Assembly   System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       47 (ms)

() => typeof(int).IsValueType   True    544 (ms)
() => TypeOf<int>.IsValueType   True    53 (ms)
() => typeof(string).IsValueType        False   889 (ms)
() => TypeOf<string>.IsValueType        False   47 (ms)

Count of warm iterations: 1000
Count of hot iterations: 10000000

() => rawType.Name      Object  221 (ms)
() => ripeType.Name     Object  42 (ms)
() => o.GetType().Name  Object  250 (ms)
() => o.GetRipeType().Name      Object  687 (ms)

() => rawType.Assembly  System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       271 (ms)
() => ripeType.Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       42 (ms)
() => o.GetType().Assembly      System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       330 (ms)
() => o.GetRipeType().Assembly  System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e       686 (ms)

() => rawType.IsValueType       False   553 (ms)
() => ripeType.IsValueType      False   47 (ms)
() => o.GetType().IsValueType   False   590 (ms)
() => o.GetRipeType().IsValueType       False   711 (ms)

Заключение

`TypeOf` и `RipeType` паттерны позволяют ощутимо улучшить производительность множественных рекурсивных вызовов в некоторых сценариях на различных CLR.

Автор: Makeman

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js