samedi 7 décembre 2019

Surprise: MethodInfor Invoke faster than direct call?

I need to invoke a generic method by MethodInfo.Invoke, because I only have generic type at runtime. I heard about MethodInfo.Invoke performance issue, that it's a lot slower than call method directly. So, to test about performance, I created benchmark program:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Benmarking
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<BenchmarkTest>();
        }
    }

    public class BenchmarkTest
    {
        private const int _count = 1000000;
        private static readonly ConcurrentDictionary<Type, Type> _typesResult = new ConcurrentDictionary<Type, Type>();
        private static readonly ConcurrentDictionary<Type, MethodInfo> _methodsInfo = new ConcurrentDictionary<Type, MethodInfo>();

        [Benchmark]
        public async Task<object> InvokeCastGenericCall()
        {
            return await ((dynamic)new Test()).GetList(new Respone<object>(), _count).ConfigureAwait(false);
        }

        [Benchmark]
        public async Task<object> DirectCall()
        {
            return await new Test().GetList(new Respone<object>(), _count).ConfigureAwait(false);
        }

        [Benchmark]
        public async Task<object> InvokeMethodCall()
        {
            var typeResult = typeof(object);

            var methodInfo = typeof(Test).GetMethod(nameof(Test.GetList)).MakeGenericMethod(typeResult);

            Task task = (Task)methodInfo.Invoke(new Test(), new object[] { new Respone<object>(), _count });

            await task.ConfigureAwait(false);

            return ((dynamic)task).Result;
        }

        [Benchmark]
        public async Task<object> InvokeMethodCall_Cache()
        {
            var typeResult = _typesResult.GetOrAdd(typeof(object), type => typeof(object));

            var methodInfo = _methodsInfo.GetOrAdd(typeResult, type => typeof(Test).GetMethod(nameof(Test.GetList)).MakeGenericMethod(type));

            Task task = (Task)methodInfo.Invoke(new Test(), new object[] { new Respone<object>(), _count });

            await task.ConfigureAwait(false);

            return ((dynamic)task).Result;
        }
    }

    public class Test
    {
        public async Task<List<T>> GetList<T>(Respone<T> input, int count) where T : class, new()
        {
            IEnumerable<T> Get()
            {
                for (var i = 0; i < count; i++)
                    yield return new T();
            }

            return await Task.FromResult(Get().ToList());
        }
    }

    public class Respone<T> where T : new()
    {

    }
}

The results were strange, MethodInfo.Invoke even faster than call method directly. And the fastest is cast object to dynamic and call dynamic method. The result is present in below image: enter image description here

I was worried about where I made the wrong benchmark? Can anyone explain it to me? Thank you very much!





Aucun commentaire:

Enregistrer un commentaire