1. 서론

MethodInfo.Invoke가 느리다는 사실은 다들 알고 계실겁니다

직접 IL을 emit하면 된다고는 하는데, 복잡하니까요...

그러다가 MethodInfo.CreateDelegate함수를 보고선 

바로 MethodInfo.Invoke(object, object?[]?)와 

MethodInfo.CreateDelegate<T>(object)로 대리자로 만든 후에 호출한것의 호출시간이 얼마나 큰 차이를 보일지 궁금해서 테스트해봤어요


2. 테스트 코드
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace MethodCallTimeTest
{
    internal class Program
    {
        private static void Main()
        {
            var program = new Program();

            BenchMark((p, _) => p.Print(), 1, program, "for jit");
            
            BenchMark(DirectCall, 1, program, "direct for jit");
            BenchMark(DynamicCall, 1, program, "dynamic for jit");
            BenchMark(ReflectionCall, 1, program, "reflection for jit");
            BenchMark(OptimizedReflectionCall, 1, program, "reflection(optimized) for jit");

            const int repeatCnt = 100000000;

            BenchMark(DirectCall, repeatCnt, program, "direct");
            BenchMark(DynamicCall, repeatCnt, program, "dynamic");
            BenchMark(ReflectionCall, repeatCnt, program, "reflection");
            BenchMark(OptimizedReflectionCall, repeatCnt, program, "reflection(optimized)");

            Console.ReadLine();
        }

        private static void BenchMark(Action<Program, int> method, int repeatCount, Program program, string header)
        {
            var watch = new Stopwatch();
            watch.Start();
            method.Invoke(program, repeatCount);
            watch.Stop();
            Console.WriteLine($"{header.PadLeft(30)} : {watch.ElapsedMilliseconds}ms");
            watch.Reset();
        }

        private static void DirectCall(Program program, int repeatCount)
        {
            for (var i = 0; i < repeatCount; ++i)
                program.Print();
        }

        private static void DynamicCall(Program program, int repeatCount)
        {
            dynamic dynProgram = program;
            for (var i = 0; i < repeatCount; i++)
                dynProgram.Print();
        }

        private static void OptimizedReflectionCall(Program program, int repeatCount)
        {
            var @delegate = typeof(Program)
                                .GetMethod(nameof(Print), BindingFlags.Instance | BindingFlags.NonPublic)
                                ?.CreateDelegate<Action>(program)
                            ?? throw new NullReferenceException();
            for (var i = 0; i < repeatCount; i++)
                @delegate.Invoke();
        }

        private static void ReflectionCall(Program program, int repeatCount)
        {
            var @delegate = typeof(Program)
                                .GetMethod(nameof(Print), BindingFlags.Instance | BindingFlags.NonPublic)
                            ?? throw new NullReferenceException();
            for (var i = 0; i < repeatCount; i++)
                @delegate.Invoke(program, Array.Empty<object>());
        }

        [method: MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
        private void Print()
        {
            _ = Math.Sqrt(Math.PI);
            _ = Math.Sqrt(Math.PI * 2);
            _ = Math.Sqrt(Math.PI * 3);
        }
    }
}

비교해보는 김에 직접 호출과 dynamic으로 호출하는것도 같이 비교해봤어요

비교 방법은 코드에도 보이다시피 제곱근 계산을 100000000 * 3회 반복하는게 다입니다


3. 실행결과

왜 이런지는 모르겠지만, 직접호출이 reflection을 통해 호출한것보다 느리게 나오네요...?