1. 서론
MethodInfo.Invoke가 느리다는 사실은 다들 알고 계실겁니다
직접 IL을 emit하면 된다고는 하는데, 복잡하니까요...
그러다가 MethodInfo.CreateDelegate함수를 보고선
바로 MethodInfo.Invoke(object, object?[]?)와
MethodInfo.CreateDelegate<T>(object)로 대리자로 만든 후에 호출한것의 호출시간이 얼마나 큰 차이를 보일지 궁금해서 테스트해봤어요
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회 반복하는게 다입니다
왜 이런지는 모르겠지만, 직접호출이 reflection을 통해 호출한것보다 느리게 나오네요...?